Deployment Checklist
Every gate between “feature-complete in Xcode” and “live in the App Store.” Don’t skip steps; each one catches a class of bug or rejection.
1. Pre-build
-
All tests passing locally (
⌘U) - No warnings in build log (treat warnings as errors)
- Latest dependencies pulled
- Git working tree clean; tagged with version number
- CHANGELOG.md updated
2. Versioning
-
MARKETING_VERSION(visible to users) bumped per semver: bug fix → patch, feature → minor, breaking → major -
CURRENT_PROJECT_VERSION(build number) bumped (monotonically increasing across all uploads) - Build number never reused for the same marketing version
- Version matches the git tag
3. Capabilities & entitlements
-
.entitlementsfile lists only what you actually use - Push notification entitlement matches APNs key configured in App Store Connect
- CloudKit container exists and is in the right environment (Development → Production promotion)
- Sign in with Apple capability registered if you use it
- App Groups configured if you have widget / share extension
4. Info.plist
-
NSCameraUsageDescriptionetc. — every privacy-sensitive API has a string that describes the actual use - Vague strings (“Used for app features”) are auto-rejected; be specific
-
CFBundleDisplayNamematches your App Store name -
LSApplicationCategoryTypeset correctly -
URL schemes registered with
LSApplicationQueriesSchemesif you query them -
ITSAppUsesNonExemptEncryption = falseif you use only standard system crypto (saves a Compliance form per build)
5. Privacy Manifest (PrivacyInfo.xcprivacy)
- File present in main app target
- All Required Reason APIs declared (file timestamp, UserDefaults, system uptime, disk space, active keyboards)
- Tracking domains listed (or empty if no tracking)
- Collected data types declared
- Third-party SDKs that ship privacy manifests are linked
6. Code signing
- Production certificate not expired
- Provisioning profile valid (preferably “Xcode Managed” automatic)
- Bundle ID matches App Store Connect record
- Bitcode disabled (Apple deprecated it; new uploads should have it off)
- dSYM uploaded for crash symbolication (Crashlytics, Sentry, or App Store Connect Crashes)
7. Performance smoke
- Cold launch on the lowest-supported device: < 2 s preferred
- Memory at idle on launch: reasonable (< 100 MB for most apps)
- No obvious main-thread blocks (test by scrolling main screens)
- Frame rate ≥ 60 FPS on the lowest-supported device
8. App Store Connect metadata
- App description (≤ 4,000 chars; preview first 2-3 lines)
- Keywords (≤ 100 chars, comma-separated)
- Promotional text (≤ 170 chars, updatable without resubmission)
- Support URL (live, not a 404)
- Marketing URL (optional but recommended)
- Privacy Policy URL (required, live)
- Age rating answered honestly
- App Privacy section (“Nutrition Label”) completed and accurate
9. Screenshots & previews
- 6.7“ iPhone (1290×2796 portrait or rotated) — required
- 6.1“ iPhone (1179×2556) — optional but recommended
- 5.5“ iPhone (1242×2208) — required for some older apps
- 12.9“ iPad (2048×2732) if you support iPad
- Mac screenshots (e.g., 1280×800 or 2560×1600) if you support Mac
- First screenshot must show the app’s value within 2 seconds of looking
- No competitor logos / brand violations
- App Preview video (15-30s) recommended for AR / animation-heavy apps
10. Pricing & availability
- Price tier selected
- In-App Purchases created in App Store Connect (and submitted with first build)
- Subscription groups configured
- Availability regions confirmed (often start US-only, expand later)
11. App Review preparation
-
App Review notes filled in with:
- Demo account credentials (if login required)
- How to access subscription / paid features in test mode
- Any non-obvious gestures or onboarding
- Reproduction steps for unusual features
- Test build runs cleanly on a fresh device (delete and reinstall to verify)
- Demo account verified working from Apple’s reviewers’ likely location (US)
12. TestFlight
- Internal testers tested the exact build you’re submitting
- External beta passed (recommended for major releases)
- Crash-free sessions > 99.5% over the beta
- No outstanding P0 / P1 bugs from beta
13. Submission
- Build uploaded via Xcode → Archive → Distribute App → App Store Connect
- Build processed in App Store Connect (~10 min after upload)
- Build attached to the version record
- “Submit for Review” clicked
- Phased release toggle decided (recommend: enabled, 7-day ramp)
- Released automatically vs manually decided
14. Post-submission
- Watch email for Review status changes (typically 24-48h)
- If rejected: read carefully, fix, resubmit promptly (don’t argue first)
- If approved: announce, monitor crashes for first 24h
- If issues: pull the version (App Store Connect → Pricing & Availability → Remove from Sale)
15. Day-1 post-launch
- Monitor Xcode Organizer → Crashes for new crash signatures
- Monitor analytics for adoption curve
- Watch App Store reviews; respond professionally to early ones
- Have a hotfix path ready (next version with build number bumped)
16. Mac-specific extras
- App notarized (Xcode does this on Archive → Distribute)
- Notarization ticket stapled
- Hardened runtime enabled
- Sandboxed (required for App Store)
- No private API usage (App Store Mac apps are sandboxed; private APIs would fail)
17. Watch-specific extras
- Independent watch app capability set
- Complications work on the watch face after install
- WatchConnectivity tested across pairing scenarios
The 10-item express checklist (small updates)
For a small bugfix release where you’re not changing capabilities:
- Bump build number
- Run tests
- Update changelog
- Archive + Distribute
- Verify build appears in App Store Connect
- Attach to a new version record
- Update Promotional Text (no resubmission required if only this changes)
- Submit for Review
- Monitor email
- Release on approval
When in doubt, work the full list. The 30 minutes you save by skipping is a week if you get rejected.