Add 'apps/mobile/' from commit '5fa30f365f21531094cd4d2045042bb4f1370ac3'

git-subtree-dir: apps/mobile
git-subtree-mainline: 86f8987dff
git-subtree-split: 5fa30f365f
This commit is contained in:
2026-08-16 21:42:59 -04:00
222 changed files with 23436 additions and 0 deletions
+88
View File
@@ -0,0 +1,88 @@
# 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 Apples [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 |