App Store Release
Pre-flight, archive to TestFlight, App Privacy labels, and the release checklist.
"Deployment" for an iOS app means shipping to the App Store — there's no server to deploy, no Docker image, no environment to promote. This page is the end-to-end path from a local Xcode build to a build in front of reviewers: pre-flight the template's placeholders, archive and upload to TestFlight, declare App Privacy honestly, and work the release checklist.
Source: docs/app-store-release-overview.md in the template. Purchase
validation against Apple's sandbox is covered separately in
Purchase Testing; RevenueCat dashboard setup
in RevenueCat Setup.
Prerequisites
- A paid Apple Developer Program account (a real device is required for TestFlight and sandbox purchase testing).
- An App Store Connect app record with a Bundle ID matching Xcode, App Store Connect, and RevenueCat.
- Completed Paid Apps agreement, bank account, and tax forms in App Store Connect (required before any in-app purchase will load).
- A RevenueCat project with an App Store app configured.
The template's default bundle identifier is:
com.soarstarter.SoarStarterSwiftChange every reference if your app uses a different Bundle ID.
Pre-flight: replace the template's placeholders
Before your first archive, walk the placeholders the template ships with. These are safe defaults for local development but must not reach the App Store:
| Area | What to change | Where |
|---|---|---|
| Bundle ID | Your reverse-DNS identifier, matching App Store Connect + RevenueCat | Xcode target, PRODUCT_BUNDLE_IDENTIFIER |
| Signing team | Your Release distribution team | Signing & Capabilities |
| Version / build | Bump the build number before every upload (1.0 (1) → 1.0 (2)) | Xcode target |
| Universal Links | Replace the applinks:example.com placeholder with your real domain and host the AASA file | Entitlement + Deep Links |
| Push environment | aps-environment = production for App Store builds | Entitlement + Push |
| Legal URLs | Real TERMS_URL / PRIVACY_URL / EULA_URL (the Consent gate and App Review both need working links) | Secrets.xcconfig |
| App icon + launch | Replace the placeholder AppIcon / launch assets | Assets (Theming) |
| Display name | PRODUCT_NAME | Xcode target |
For TestFlight and App Store builds, REVENUECAT_IOS_KEY must be the App
Store app public SDK key (starts with appl_). RevenueCat intentionally
crashes Release builds that use a simulated-store key (starts with
test_). See Purchase Testing.
Archive and upload to TestFlight
Prepare the target
Open SoarStarterSwift.xcodeproj. Under Signing & Capabilities select your
Release team and confirm the Bundle Identifier matches App Store Connect.
Increase the build number — every upload needs a higher build than the last.
Archive
Select a generic iOS device (or a real device) as the run destination, then
run Product → Archive. xcodebuild archiving is also fine, but Organizer is
the smoother path for distribution.
Distribute
In Organizer, select the archive → Distribute App → App Store Connect → Upload, and continue through signing.
If Xcode reports Upload Symbols Failed — The archive did not include a dSYM for Sentry.framework, the upload still succeeds. It only affects crash
symbolication for that framework, not installation or purchases — see
Observability.
TestFlight
In App Store Connect → your app → TestFlight, wait for processing, resolve Missing Compliance if shown, create an internal testing group, add the build, and install from the TestFlight app on a device.
For encryption compliance, if the app only uses standard system networking
(HTTPS, Apple sign-in, RevenueCat, Supabase, Sentry, PostHog), answer that it
does not use non-exempt proprietary encryption. You can pre-declare this in
Info.plist:
<key>ITSAppUsesNonExemptEncryption</key>
<false/>App Privacy labels
App Review requires you to declare what data the app collects. Map it to what the template's SDKs actually do — don't over- or under-declare:
| SDK | Collects | Notes |
|---|---|---|
| Supabase | Account data (email, user ID), plus whatever your tables store | Linked to identity |
| RevenueCat | Purchase history, a device/user identifier | Linked to identity |
| PostHog | Product analytics / usage events | Consent-gated — nothing sent before the Consent gate; can be disabled |
| Sentry | Crash data, diagnostics | Used for app functionality |
Because analytics only start after the consent gate, and both PostHog and Sentry
no-op without keys, your labels should reflect your configured build — if you
ship without a POSTHOG_API_KEY, you're not collecting analytics.
Review notes
Two things App Review specifically looks for, both already wired in the template:
- Account deletion is available in-app — the
delete-accountEdge Function behind Account & Security (Example Features). Apple requires any app with account creation to offer in-app deletion. Point the reviewer to it. - The consent gate blocks tracking until the user agrees — mention it if asked about the App Privacy labels.
If your app gates features behind a subscription, provide a demo account or a note on how to reach the paywall so the reviewer can exercise it.
First submission: in-app purchase
The first in-app purchase must be submitted with a new app version. App Store Connect will say:
Your first in-app purchase must be submitted with a new app version.Attach the in-app purchase on the app version page before submitting. For
TestFlight sandbox testing alone, a product in Ready to Submit state is
usually enough once agreements, tax, banking, product metadata, and RevenueCat
are correct.
Release checklist
- Bundle ID / signing team / build number set for Release.
-
REVENUECAT_IOS_KEYis theappl_App Store key, not atest_key. -
REVENUECAT_PRO_ENTITLEMENT_IDexactly matches the RevenueCat entitlement identifier (template default:pro). - Products (
soar_starter_swift_pro_monthly/_pro_yearly/_lifetime) areReady to Submit, Cleared for Sale, with price + metadata + review screenshot. -
aps-environment=productionand the RevenueCat webhook points at the production Supabase project (Supabase Setup). -
applinks:entitlement uses your real domain with a hosted AASA file. - Legal URLs, app icon, and display name are yours, not placeholders.
- Update-gate strategy set:
MIN_APP_VERSION/LATEST_APP_VERSIONandAPP_STORE_URL(fillAPP_STORE_URLonce the App Store ID exists) — see App Gates. - App Privacy labels match the SDKs your build actually configures.
- In-app account deletion verified working for the reviewer.
Related pages
Purchase Testing
Sandbox testers and validating a purchase against the subscriptions row.
RevenueCat Setup
Dashboard-side products, entitlement, offering, and webhook.
Troubleshooting
Release-time failures: crash-on-launch, offerings empty, Missing Compliance.
Configuration
The xcconfig keys this checklist references.
