iOS — first TestFlight build, on the Mac
For the first physical iPhone 16 install and Safari/app performance comparison, start with IPHONE-PERFORMANCE.md. Xcode can install directly before TestFlight is configured. The recorder is available in both runtimes under Options → System → Performance test.
The iOS project is generated and pre-configured in this repo; the only things that need a Mac are signing, archiving and uploading. Budget an hour the first time, ten minutes after that.
Before Xcode (App Store Connect, once)
- App ID. developer.apple.com → Certificates, Identifiers & Profiles →
Identifiers → "+" → App IDs → App. Bundle ID
com.verybad.hallpass(explicit). Capabilities: tick In-App Purchase. (Push Notifications later, when OneSignal has keys.) - App record. appstoreconnect.apple.com → Apps → "+" → New App.
Platform iOS, name HALL PASS, primary language English (U.S.), bundle ID
from step 1, SKU
hallpass. - Paid Applications agreement. Agreements, Tax and Banking. Until this is Active, in-app purchases do not work even in sandbox, and the store inside the app will say it did not answer.
- In-app purchases (Monetization → In-App Purchases):
supporter(non-consumable),boost_7d,boost_30d(consumables). Names, descriptions and a screenshot each are required before review, not before TestFlight. - Sandbox tester (Users and Access → Sandbox): one account you can sign into on the test phone, so purchases charge nothing.
On the Mac
git pull
npm ci
npm run ios:prepare # production build, complete runtime assets, Capacitor sync
npm run ios:open # opens ios/App/App.xcodeproj in Xcode
In Xcode:
- Select the App target → Signing & Capabilities → Team: your Apple
Developer team. Automatic signing. Bundle id is already
com.verybad.hallpass. - "+ Capability" → In-App Purchase.
- Product → Destination → Any iOS Device (arm64).
- Product → Archive. First archive resolves SwiftPM packages (Capacitor, AdMob, OneSignal, RevenueCat, the local ad-tracker plugin); expect a few minutes.
- Organizer opens on success → Distribute App → App Store Connect →
Upload. Accept the defaults. The export-compliance question is already
answered in Info.plist (
ITSAppUsesNonExemptEncryption = false), so it should not ask. - appstoreconnect.apple.com → the app → TestFlight. The build appears after processing (5–20 min). Add Internal Testing → a group with yourself and friends who are App Store Connect users. No review, no minimum count. External testers need a one-time Beta App Review (a day or so).
Testers install the TestFlight app, accept the invite, and get every new build automatically.
What is already set in the project
- Landscape only, iPhone and iPad, full-screen (no iPad multitasking), status bar hidden — the same lock Android has.
- AdMob app id (Google's sample id until the real one is pasted),
NSUserTrackingUsageDescription, Google's full SKAdNetwork list. ios.handleApplicationNotifications = falsefor OneSignal.- Deployment target iOS 15, version 1.0 (1). Bump
CURRENT_PROJECT_VERSIONin the target's General tab for each upload — App Store Connect refuses a duplicate build number.
If the archive fails
- RevenueCat / ad-tracker Swift errors.
plugins/hallpass-ad-trackerhas not yet been compiled on a Mac. Capture the first full compiler error so the bridge can be fixed against the installed SDK. Do not remove the plugin from the generated Swift package as a substitute for verification. - "No signing certificate." Xcode → Settings → Accounts → your Apple ID → Download Manual Profiles, or just let Automatic signing create one.
- SwiftPM cannot resolve a package. File → Packages → Reset Package Caches, then archive again.
Keys to paste when you have them
src/services.js: revenuecat.ios (appl_…), admob.ios.app + the two
unit ids (and the same app id in ios/App/App/Info.plist under
GADApplicationIdentifier), onesignal.appId. Rebuild, npx cap sync ios,
archive, upload.
Before submitting for App Store review (not needed for TestFlight)
Screenshots for 6.9" and 6.5" iPhones (and iPad if kept), the App Privacy
answers from store/LISTING.md, the privacy URL hallpass.cc/privacy, age
rating questionnaire, review notes that say ads are AdMob and purchases are
via RevenueCat with a sandbox account provided. Set admob.testing = false
with real unit ids first.