Capstone · Milestone 3 — App Store and TestFlight submission
Unit 31 · Capstone · Milestone 3. You ship. Provisioning, signing, App Store Connect, an archive to TestFlight, and — if you choose — a submission for App Store review. "It runs on my Mac" is not shipped; a build a stranger can install on their phone is.
Shipping is a skill, and it is the one most self-taught developers never practice. The code being done is maybe 70% of the way to a user holding your app. This milestone covers the other 30%: the signing, the metadata, and the review gauntlet.
Step 1 — the Apple Developer account and identifiers
You need an Apple Developer Program membership. Then, in the developer portal / Xcode:
- A Bundle ID that uniquely identifies your app (e.g.
com.yourname.tracker). - Signing: let Xcode manage signing automatically for a capstone — it creates the development and distribution certificates and provisioning profiles for you. Manual signing is a thing you learn later when a CI pipeline needs it.
- Any capabilities your app declares (push, background modes, App Groups) must be enabled on the App ID, or the upload is rejected before review even starts.
Signing failures are the most common wall at this stage, and they are almost always a mismatch: the capability is on in Xcode but not on the App ID, or the bundle id does not match the App Store Connect record. Read the error — Xcode's signing errors are specific — and fix the mismatch rather than regenerating certificates at random.
Step 2 — the App Store Connect record
In App Store Connect, create the app record: name, primary language, bundle id, and SKU. Then fill the metadata review requires:
- Screenshots for the required device sizes (a couple of clean captures from the simulator).
- App description, keywords, support URL, and a privacy policy URL (required — even a simple hosted page).
- App Privacy answers: what data you collect and why. Answer honestly; the "nutrition label" is generated from this and Apple checks it against your behavior.
- Age rating questionnaire.
Step 3 — archive and upload
In Xcode, select a real device or "Any iOS Device," then Product → Archive. From the Organizer, Distribute App → App Store Connect → Upload. The build is processed server-side (a few minutes to an hour) and then appears in TestFlight.
- Bump the build number on every upload; App Store Connect rejects a duplicate.
- Ensure the version matches your marketing version.
- Fix any upload-time validation errors (missing icons, an invalid
Info.plistkey, a disallowed API) — these are caught before review.
Step 4 — TestFlight
TestFlight is your staging ground. Install the build on your own device and test the release build, which behaves differently from a debug build (optimizations on, no debugger). Add internal testers (your own accounts) and, optionally, external testers — external testing requires a lightweight Beta App Review first. TestFlight alone satisfies "shipped" for the capstone; App Store review is the stretch.
Always test the actual TestFlight build before submitting for review. Release-configuration bugs — something that only breaks with optimizations, or a resource that was only in the debug bundle — are real and are exactly what a reviewer will hit. Catch them in TestFlight, not in a rejection.
Step 5 — submit for review and pass it
If you go for the App Store, submit from App Store Connect and expect a review within a day or two. The common rejection reasons, most to least frequent for a capstone:
- Crashes / bugs on review. Reviewers exercise the app on real hardware. A crash on launch or an obvious broken flow is an instant rejection. Test the release build first.
- Incomplete metadata or a broken privacy-policy / support URL. Links must resolve.
- Guideline 4.2 — minimum functionality. An app that is too thin ("just a wrapped webpage," or a single static screen) gets rejected. A focused, genuinely useful app clears this; make sure yours does something.
- Privacy mismatches. Your App Privacy answers must match what the app actually does. If you say you collect nothing but you call an analytics SDK, that is a rejection.
- Sign-in walls. If you gate content behind login, provide a demo account in the review notes or reviewers cannot get in.
- Placeholder content. "Lorem ipsum," dead buttons, and TODO screens read as unfinished.
Respond to a rejection professionally: read the cited guideline, fix the specific issue, reply in Resolution Center with what you changed, and resubmit. A rejection is a normal step, not a verdict.
Checkpoint
You should now have:
- A signed distribution build uploaded to App Store Connect.
- The app installable via TestFlight on a real device, tested in the release configuration.
- Complete, truthful metadata and privacy answers.
- Either a live App Store submission in review (stretch) or a TestFlight build ready to share.
Stretch goals
- Automate the archive/upload with
xcodebuildand Fastlane or App Store Connect API so shipping is one command — the same "automate the guardrails" instinct applied to release. - Write real release notes and a short App Store description that reads like a product, not a changelog.
- Add a crash reporter (MetricKit or a service) so you learn about release-build crashes from the field, closing the loop on the thing reviewers care most about.
Knowledge check
Q: What is the most common reason a capstone-grade app is rejected, and how do you prevent it? A crash or obvious bug hit during review, because reviewers run the release build on real hardware. Prevent it by testing the TestFlight (release-configuration) build on a device before submitting — release builds behave differently from debug builds, and that difference is where these bugs live.
Q: Why does TestFlight count as "shipped" for the capstone when the App Store is the stretch? Because TestFlight produces a signed, release build a stranger can install on their own phone — the real bar the milestone cares about. App Store review adds public distribution and the guideline gauntlet, which is valuable but orthogonal to proving you can produce a real, installable build.