Guide · 9 min read · Updated 2026-10-06
Expo App Store submission checklist
1. Identity: name, bundle identifier and package
Your iOS bundle identifier and Android package name are permanent once you upload a build. Choose them deliberately, based on a domain you control.
- expo.name is set to the name you want under the icon.
- expo.ios.bundleIdentifier is set, e.g. com.yourdomain.appname — not com.example, com.anonymous or host.exp.exponent.
- expo.android.package is set and follows the same reverse-domain convention.
- The bundle identifier matches the App ID you registered in your Apple Developer account and the app record in App Store Connect.
- Firebase, RevenueCat, Google Sign-In and OAuth providers are configured with the same identifiers.
2. Versioning and build numbers
App Store Connect rejects an upload that reuses a build number for the same version. Google Play requires each upload to have a higher versionCode.
- expo.version is set using semantic versioning (e.g. 1.0.0).
- Either set "cli": { "appVersionSource": "remote" } in eas.json and "autoIncrement": true in the production profile, or manage ios.buildNumber and android.versionCode manually.
- If you use EAS Update, expo.runtimeVersion is configured so updates only reach compatible builds.
3. EAS build profiles
- eas.json exists and contains a build.production profile.
- The production profile does not set developmentClient: true.
- The production profile does not use distribution: "internal" or ios.simulator: true.
- Android production builds produce an AAB (the default), not an APK.
- Environment variables for production (API URLs, public keys) are set per profile, not hard-coded.
- Run npx expo install --fix so React Native and Expo packages match your SDK.
4. Icons, splash and display
- expo.icon points to a 1024×1024 PNG with no transparency, and the file exists.
- expo.android.adaptiveIcon has a foregroundImage with safe-zone padding and a backgroundColor.
- A splash screen is configured (the expo-splash-screen plugin on SDK 52+).
- expo.scheme is set if you use Expo Router, deep links or OAuth redirects.
5. Permissions and purpose strings
iOS requires a purpose string for every protected resource you request, and App Review reads them. Each should explain specifically why your app needs access — “We need your camera” is weaker than “Scan receipts to add expenses”.
- Camera → NSCameraUsageDescription (or the expo-camera plugin’s cameraPermission).
- Photo library → NSPhotoLibraryUsageDescription (expo-image-picker photosPermission).
- Microphone → NSMicrophoneUsageDescription, if you record audio or video.
- Location → NSLocationWhenInUseUsageDescription; justify any background location.
- Contacts, calendars → NSContactsUsageDescription, NSCalendarsUsageDescription.
- Remove Android permissions you don’t use, especially SMS, call log, QUERY_ALL_PACKAGES and background location.
6. Privacy: tracking, manifests and labels
- If any SDK tracks users across apps or websites (e.g. personalised ads), request App Tracking Transparency permission first and include NSUserTrackingUsageDescription.
- Configure expo.ios.privacyManifests with the required-reason APIs your app and SDKs use.
- Complete App Privacy details in App Store Connect to reflect analytics, crash reporting and advertising SDKs.
- Publish a privacy policy URL and add it to App Store Connect.
- If your app supports account creation, provide in-app account deletion.
7. In-app purchases and subscriptions
Apple requires digital goods and subscriptions to use in-app purchase. Reviewers will try to buy, cancel and restore.
- A visible Restore Purchases button exists, typically on the paywall and in settings.
- Purchase calls handle user cancellation and errors without leaving the UI stuck.
- Premium state is derived from entitlements, not a hard-coded flag.
- RevenueCat is configured with the platform public keys (appl_ / goog_), not a test or secret key.
- Subscription terms, price and a link to your terms and privacy policy are shown on the paywall.
- Products are “Ready to Submit” in App Store Connect and attached to the version under review.
8. Security and production configuration
- No secret keys in the app: Supabase service-role keys, Stripe sk_live_ keys, private keys or service-account JSON.
- No EXPO_PUBLIC_ variable holds a secret — Expo inlines them into the bundle.
- Tokens are stored in expo-secure-store, not AsyncStorage.
- No localhost, LAN or ngrok URLs outside __DEV__ guards; all endpoints use HTTPS.
- No AdMob test ad unit IDs outside __DEV__, and the AdMob app ID is configured.
- Debug and mock flags are derived from __DEV__ or build profiles, not hard-coded to true.
9. App Store Connect essentials
- Screenshots for the required device sizes, reflecting the current UI.
- Description, keywords, support URL and age rating questionnaire completed.
- Export compliance: set ios.config.usesNonExemptEncryption appropriately to skip the per-build question.
- A demo account and notes for App Review if your app requires sign-in.
- Test the production build on a real device via TestFlight before submitting.
Before you submit
This checklist reflects common submission requirements as generally documented by Apple and Expo. Requirements change; always confirm against Apple’s current App Review Guidelines and Expo’s documentation for your SDK version. Passing this checklist does not guarantee approval.