# Local iOS release (no EAS) Archive and upload **beenvoice** to App Store Connect using Xcode on your Mac — no Expo Application Services (EAS) subscription required. ## Prerequisites - macOS with **Xcode** (same major version you use for development) - **Apple Developer Program** membership - App record in [App Store Connect](https://appstoreconnect.apple.com) with bundle ID `com.beenvoice.app` - **Distribution** signing set up in Xcode (automatic signing + team is enough for most cases) - [App Store Connect API key](https://appstoreconnect.apple.com/access/integrations/api) (for upload only) ## One-time setup ```bash cd beenvoice-app cp .ios-release.env.example .ios-release.env ``` Edit `.ios-release.env`: | Variable | Where to find it | |----------|------------------| | `APPLE_TEAM_ID` | [developer.apple.com/account](https://developer.apple.com/account) → Membership → Team ID | | `APP_STORE_CONNECT_API_KEY_ID` | App Store Connect → Users and Access → Integrations → Keys | | `APP_STORE_CONNECT_API_ISSUER_ID` | Same page (Issuer ID at top) | | `APP_STORE_CONNECT_API_KEY_PATH` | Path to downloaded `AuthKey_XXXXXX.p8` | | `EXPO_PUBLIC_API_URL` | Production API URL baked into the release bundle | Optional: store the `.p8` in `~/.appstoreconnect/private_keys/` (never commit it). Open the iOS project once in Xcode and confirm **Signing & Capabilities** succeeds for targets **beenvoice** and **ExpoWidgetsTarget**. ## Commands ```bash # Archive + export signed IPA to dist/ios-release/export/ bun run ios:release # Archive + export + upload to App Store Connect (TestFlight) bun run ios:release:upload ``` ### Flags (pass through to the script) ```bash bash scripts/ios-release.sh --archive-only # .xcarchive only bash scripts/ios-release.sh --export-only --upload # re-upload existing archive bash scripts/ios-release.sh --no-prebuild # skip expo prebuild bash scripts/ios-release.sh --no-bump # don't increment build number ``` With `IOS_BUMP_BUILD=1` in `.ios-release.env`, each run bumps `CFBundleVersion` via `agvtool` (recommended for repeated TestFlight uploads). ## What the script does 1. `expo prebuild --platform ios` (unless `--no-prebuild`) 2. `pod install` 3. Optional build-number bump (`agvtool`) 4. `xcodebuild archive` (Release, generic iOS device) 5. `xcodebuild -exportArchive` → App Store IPA 6. `xcrun altool --upload-app` (with `--upload` only) Artifacts land in `dist/ios-release/` (gitignored). ## After upload 1. App Store Connect → **TestFlight** — wait for “Processing” to finish 2. Smoke-test on device 3. Submit for App Store review when ready See also [APP_STORE_CONNECT.md](./APP_STORE_CONNECT.md) for metadata, screenshots, and review notes. ## Notes - **Dev client:** `expo-dev-client` is in the native project today. Store builds still work, but the binary includes the dev client shell. For a slimmer production binary, remove that plugin and re-run prebuild before release (or maintain a separate `app.config` variant). - **Manual upload:** After `bun run ios:release`, drag the IPA into Apple’s [Transporter](https://apps.apple.com/app/transporter/id1450874784) app instead of using `--upload`. - **CI:** Run the same script on a Mac runner (GitHub `macos-latest`, etc.) with secrets injected as env vars instead of `.ios-release.env`. ## Troubleshooting | Issue | Fix | |-------|-----| | No signing certificate | Xcode → Settings → Accounts → Download Manual Profiles; or open project and enable automatic signing | | `pod install` fails | `cd ios && pod repo update && pod install` | | Upload auth error | Verify API key has **Developer** access; check Key ID, Issuer ID, and `.p8` path | | Duplicate build number | Enable `IOS_BUMP_BUILD=1` or bump `CURRENT_PROJECT_VERSION` in Xcode | | Widget extension signing | Both **beenvoice** and **ExpoWidgetsTarget** need the same team |