Chapter 15: TestFlight
The finish line. Your app goes from Xcode to real iPhones via TestFlight.
This is the chapter where your app leaves your computer and lands on other people's phones. We'll create a minimal app record in App Store Connect, archive the app, upload it, and invite testers through TestFlight. By the end of this chapter, someone else will be using your app.
What You'll Do
- Create a minimal app record in App Store Connect
- Make a production build of the web app
- Set version and build numbers in Xcode
- Archive and upload the app
- Invite testers via TestFlight
- See what the tester experience looks like
Step 1: Create the App Record in App Store Connect
Before you can upload a build, Apple needs to know your app exists. This is a minimal setup — just enough to accept a TestFlight build.
- Go to appstoreconnect.apple.com
- Sign in with your Apple ID
- Click My Apps
- Click the + button → New App
- Fill in the form:
| Field | Value |
|---|---|
| Platforms | iOS |
| Name | GratitudeTree |
| Primary Language | English (U.S.) |
| Bundle ID | Select com.yourname.gratitudetree from the dropdown |
| SKU | gratitudetree (any unique identifier — not shown to users) |
- Click Create
That's it for now. You don't need to fill in descriptions, screenshots, privacy policies, or any other metadata to use TestFlight. We'll handle all of that in the next chapter when preparing for the actual App Store.
If your Bundle ID doesn't appear in the dropdown
This means Xcode hasn't registered it yet. Go back to Xcode, make sure automatic signing is enabled with your team, and build once (Cmd + R). The Bundle ID will be registered automatically. Then refresh App Store Connect.
Step 2: Production Build
Your app currently loads from the Vite dev server (localhost:5173). For TestFlight, it needs to load from bundled files instead.
$ claude "Prepare our app for a TestFlight build. Make sure the Capacitor config doesn't h..."Pre-flight checklist
| Check | How to verify |
|---|---|
| Dev server config removed | capacitor.config.ts has no server block |
| Production build works | npm run build succeeds with no errors |
| App works from bundled files | npx cap sync → run in Simulator → app loads (no blank screen) |
| Signing is configured | Xcode → Signing & Capabilities → green checkmarks |
| Bundle ID matches | Same in Xcode AND App Store Connect |
| App icon is set | Visible on the Simulator home screen |
Run the production build
npm run build
npx cap sync
Then test in the Simulator — open Xcode (npx cap open ios), select an iPhone simulator, and press Cmd + R. The app should load normally without a dev server running.
Step 3: Set Version and Build Number
In Xcode, select the App target → General tab:
| Field | Value | What it means |
|---|---|---|
| Version | 1.0.0 | The marketing version users see (e.g., "Version 1.0.0") |
| Build | 1 | An internal number that must increase with each upload |
Version vs Build number rules
- Version (e.g.,
1.0.0): Follows semantic versioning. Visible to users. You decide when to bump it. - Build (e.g.,
1,2,3): Must be unique for each upload. Even if you don't change the version, you must bump the build number.
Version 1.0.0, Build 1 → First TestFlight upload
Version 1.0.0, Build 2 → Bug fix upload (same version, new build)
Version 1.1.0, Build 3 → Feature update (new version, new build)
For your first upload: Version 1.0.0, Build 1.
Step 4: Archive the App
Archiving creates a release-ready bundle of your app.
- In Xcode, select Any iOS Device (arm64) as the build destination (not a Simulator)
- Go to Product → Archive
- Wait for the archive to complete (this takes 1-3 minutes)
- The Organizer window opens automatically showing your archive
This happens when a Simulator is selected as the build destination. You must select Any iOS Device (arm64) from the device dropdown. You don't need a physical device connected — "Any iOS Device" targets all real devices.
Step 5: Upload to App Store Connect
From the Organizer window (after archiving):
- Select your archive
- Click Distribute App
- Select App Store Connect → Upload
- Leave all options at their defaults:
- Strip Swift symbols: Yes
- Upload symbols: Yes
- Manage version and build number: Yes (Xcode auto-increments)
- Click Upload
- Wait for validation and upload (2-5 minutes)
If validation fails, Xcode shows the specific error. Fix it and archive again.
If you hit archive or upload errors, paste the full error message into Claude Code. It's particularly good at diagnosing signing issues, missing entitlements, and binary validation failures.
Step 6: Wait for Processing
After a successful upload:
- Go to appstoreconnect.apple.com
- Select your app → TestFlight tab
- You'll see your build with status "Processing"
Processing typically takes 5-30 minutes. Apple is scanning for malware, checking for private API usage, and generating device-specific builds.
When processing finishes, the status changes to Ready for Testing.
Step 7: Export Compliance
After processing, Apple may ask about export compliance. For GratitudeTree:
"Does your app use encryption?"
Select Yes — your app uses HTTPS (which is encryption). Then:
"Is it exempt?"
Select Yes — standard HTTPS/TLS for network communication is exempt from export compliance documentation.
This is a one-time question per app. After you answer, future builds skip this step.
Step 8: Invite Internal Testers
Internal testers are members of your Apple Developer team. They get builds immediately — no review required.
- In App Store Connect → TestFlight tab
- Click Internal Testing in the sidebar
- Click + to create a new group (e.g., "Core Team")
- Add testers by their Apple ID email
- Select the build to test
- Click Start Testing
Testers receive an email with a link to install the TestFlight app and your beta.
What testers see
- They get an email: "You've been invited to test GratitudeTree"
- They install the TestFlight app from the App Store (if they don't have it)
- They tap Accept in the TestFlight invite
- Your app appears in TestFlight → they tap Install
- Your app is on their home screen — with an orange dot indicating it's a TestFlight build
Step 9: Add External Testers (Optional)
External testers are people outside your team. External builds require a light App Review (usually approved within 24-48 hours).
- In TestFlight → External Testing in the sidebar
- Click + to create a group (e.g., "Beta Testers")
- Add testers by email or share a public link
- Select a build
- Fill in Test Information:
- What to Test: "Try creating text and photo entries. Shake your phone to discover a random past entry. Check the streak counter on the Profile tab."
- Beta App Description: Short description of the app
- Contact email: Your email for feedback
- Submit for Beta App Review
Public TestFlight links
Instead of adding testers by email, you can generate a public link that anyone can use to join your beta (up to 10,000 testers). This is great for sharing on social media or in communities.
Step 10: What Testers Should Test
Send your testers a message with specific things to try:
Thanks for testing GratitudeTree! Here's what to try:
1. Sign up with your email
2. Create a text entry about something you're grateful for
3. Create a photo entry using the camera or library
4. Go to Profile — check your streak and stats
5. Shake your phone — a random past entry should appear with haptic feedback
6. Log out and back in — your entries should persist
If anything breaks or feels wrong, let me know!
Testers can submit feedback directly through the TestFlight app — including screenshots — which appears in App Store Connect.
Checkpoint
Your app should now:
- A minimal app record exists in App Store Connect (name + Bundle ID)
- A production build is created with
npm run build && npx cap sync - The app runs in the Simulator without a dev server
- Version is set to 1.0.0 and build to 1
- The app is archived and uploaded to App Store Connect
- The build has finished processing
- Export compliance questions are answered
- At least one tester is invited and can install the app via TestFlight
Your App Is on TestFlight.
Other people are using something you built. That's a real milestone — most developers never get this far.
In the next chapter, we'll cover everything you need to prepare for an actual App Store submission — privacy policy, screenshots, metadata, and more. That step is entirely optional. Your app is already in people's hands.
Follow @parvsondhi for build threads, tips, and updates on this tutorial.