Enable App Store builds without EAS, iOS 18 App Intents plugins, and signing fixes for distribution export. Add mobile invoice PDF preview, compact line items, and more reliable shortcut deep-link handling. Co-authored-by: Cursor <cursoragent@cursor.com>
3.8 KiB
3.8 KiB
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 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 (for upload only)
One-time setup
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 → 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
# 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 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
expo prebuild --platform ios(unless--no-prebuild)pod install- Optional build-number bump (
agvtool) xcodebuild archive(Release, generic iOS device)xcodebuild -exportArchive→ App Store IPAxcrun altool --upload-app(with--uploadonly)
Artifacts land in dist/ios-release/ (gitignored).
After upload
- App Store Connect → TestFlight — wait for “Processing” to finish
- Smoke-test on device
- Submit for App Store review when ready
See also APP_STORE_CONNECT.md for metadata, screenshots, and review notes.
Notes
- Dev client:
expo-dev-clientis 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 separateapp.configvariant). - Manual upload: After
bun run ios:release, drag the IPA into Apple’s Transporter 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 |