Ship It Checklist for the App Store. You built an iOS app with Expo, React Native, Swift, Cursor, Claude Code, Capacitor or FlutterFlow, and you are ready to submit it.
Unlock the rest of the checks
Two checks in each section are open, including why they matter, how to check them, and the prompt. Every other check keeps its title on the page. The reason, the steps and the prompt for those checks are omitted until you pay. One payment of £10 opens every stack. Access does not expire.
Or pay £15 once for the same access and the AI-Ready Pack. That price is the checklist price plus the pack price. The pack does not change your ranking.
Card details go to Stripe, not to this form. After payment, this browser gets a cookie. Restore access sends a one-hour link if you need another device. Error fixes stay free.
Only need this checklist? The App Store submission checklist is £10 on its own and does not open the other stacks. The payment above opens every stack, including this one.
0 of 26 done
Ticks are saved in this browser only.
Accounts and setup
Critical
App Store Connect will not accept a build until the Apple Developer Program membership is active for the account that will sell the app. A personal team in Xcode can sign a local build. It cannot ship to the store. The seller name on the membership is the name people see on the product page, so it needs to be the name you intend to publish under.
You are working in this iOS project. Do not add features.
Goal: The project is tied to the Apple team that will sell the app, not a personal development team.
The project may be Expo, React Native, Swift, a Capacitor wrapper, or a FlutterFlow export. Change only files that already exist.
Steps:
1. Find the bundle identifier and the DEVELOPMENT_TEAM or Expo owner value.
2. Leave a clear TODO where the real team ID should go if it is still a personal team. Do not invent a team ID.
3. If the app uses EAS or Fastlane, point the submit profile at the production Apple team once I paste the team ID.
4. Do not print certificates, private keys, API keys, or App Store Connect passwords.
5. Tell me which files you changed, and list anything I still have to do in Xcode or App Store Connect.
High
The bundle ID is permanent for that App Store record. A generated ID such as com.example.app, or one copied from a tutorial, will be rejected or will belong to someone else's account. Expo, FlutterFlow and Capacitor all write a default. Replace it before the first upload, because changing it later means a new app.
You are working in this iOS project. Do not add features.
Goal: The iOS bundle identifier is a reverse-DNS ID for this product, used in every native config.
The project may be Expo, React Native, Swift, a Capacitor wrapper, or a FlutterFlow export. Change only files that already exist.
Steps:
1. Find every iOS bundle identifier, including app.json, Info.plist, the Xcode project, capacitor.config, and any Flutter project file.
2. If I have not given you the final ID, stop and ask. Do not invent a domain.
3. When I give you the ID, set it in every place the iOS target reads, and leave Android applicationId alone unless I ask.
4. Do not print certificates, private keys, API keys, or App Store Connect passwords.
5. Tell me which files you changed, and list anything I still have to do in Xcode or App Store Connect.
Critical
The reason, the checks and the prompt for this item are paid.
An iOS binary can be unpacked. A Supabase service-role key, a database password, or an admin token inside the app is then public. The app should ship only keys that are safe for a client, such as a Supabase anon key, and the server should enforce access.
You are working in this iOS project. Do not add features.
Goal: No service-role, secret, or admin key is compiled into the iOS app.
The project may be Expo, React Native, Swift, a Capacitor wrapper, or a FlutterFlow export. Change only files that already exist.
Steps:
1. Search for service_role, secret keys, and client-exposed env prefixes.
2. Move any privileged call to a server you control. The app should send the user's own session, not an admin key.
3. If a real key was in the repo or the bundle, tell me which key to rotate. Do not print the value.
4. Do not print certificates, private keys, API keys, or App Store Connect passwords.
5. Tell me which files you changed, and list anything I still have to do in Xcode or App Store Connect.
Critical
An OpenAI, Anthropic or other model key inside an Expo or Swift app will be extracted, and the bill comes to you. The app should call your own endpoint. That endpoint holds the provider key and checks that the caller is allowed to spend it.
You are working in this iOS project. Do not add features.
Goal: The iOS app calls our server for model requests. The provider key stays on the server.
The project may be Expo, React Native, Swift, a Capacitor wrapper, or a FlutterFlow export. Change only files that already exist.
Steps:
1. Find client calls to model providers and the key they send.
2. Replace them with a call to a server route I name, sending the user session rather than the provider key.
3. On that server route, read the provider key from server-only configuration and reject unauthenticated calls.
4. Do not print the provider key. Tell me if one was previously shipped in the app so I can rotate it.
5. Do not print certificates, private keys, API keys, or App Store Connect passwords.
6. Tell me which files you changed, and list anything I still have to do in Xcode or App Store Connect.
High
The reason, the checks and the prompt for this item are paid.
Guideline 5.1.1(v) says that if the app supports account creation, it must also let people start account deletion in the app. A link that only opens a website, or a mail-to address with no in-app step, is a common rejection. Reviewers look for the deletion control inside the app, not only on a website you host. Deletion has to remove the account and the data you are not required to keep.
You are working in this iOS project. Do not add features.
Goal: A signed-in person can start account deletion inside the app, and the server deletes that account.
The project may be Expo, React Native, Swift, a Capacitor wrapper, or a FlutterFlow export. Change only files that already exist.
Steps:
1. Add a delete-account action on the account screen. Ask for confirmation, then call a server route with the user session.
2. The server should delete the account and the personal data you are not required to keep. Do not only hide the row.
3. If Sign in with Apple is enabled, leave a clearly marked step for revoking the Apple token, and tell me what credential that call needs.
4. Do not make email-to-support the only way to delete the account.
5. Do not print certificates, private keys, API keys, or App Store Connect passwords.
6. Tell me which files you changed, and list anything I still have to do in Xcode or App Store Connect.
Guideline 3.1.1 says you need a restore mechanism for restorable in-app purchases. People reinstall, or they buy on one device and open another. If the paywall has no Restore Purchases control, review often stops there. The control should call StoreKit restore, or the StoreKit 2 current entitlements path, and then unlock what that Apple ID already owns.
You are working in this iOS project. Do not add features.
Goal: Every paywall includes a Restore Purchases action wired to StoreKit.
The project may be Expo, React Native, Swift, a Capacitor wrapper, or a FlutterFlow export. Change only files that already exist.
Steps:
1. Find each screen that sells a subscription or a non-consumable.
2. Add a Restore Purchases button that calls the existing store code. Do not invent a second billing system.
3. On success, unlock the same content a fresh purchase unlocks. On failure, show the StoreKit message.
4. Do not grant access when the restore returns no transactions.
5. Do not print certificates, private keys, API keys, or App Store Connect passwords.
6. Tell me which files you changed, and list anything I still have to do in Xcode or App Store Connect.
Guideline 5.1.1(i) says every app needs a privacy policy link in the App Store Connect metadata and inside the app, where people can find it. The policy has to say what you collect, how you collect it, what you do with it, who you share it with, and how someone can ask you to delete it. A placeholder domain, or a URL that 404s, blocks the submission.
You are working in this iOS project. Do not add features.
Goal: The app links to a privacy policy URL, and the settings screen shows that same link.
The project may be Expo, React Native, Swift, a Capacitor wrapper, or a FlutterFlow export. Change only files that already exist.
Steps:
1. Add a Privacy row in settings that opens the URL I provide. If I have not provided one, use a clearly marked placeholder and tell me to replace it before submission.
2. Do not invent a company address or a list of SDKs you cannot find in the project.
3. List the SDKs and network calls you did find so I can check the policy against them.
4. Do not print certificates, private keys, API keys, or App Store Connect passwords.
5. Tell me which files you changed, and list anything I still have to do in Xcode or App Store Connect.
App Store Connect asks for an App Privacy label, and guideline 2.3 says metadata, including privacy information, must reflect the app. A label that says you collect nothing, while the app sends email, photos or prompts to a server, is a review problem and a trust problem. Fill the label from the build, not from a template that said 'data not collected'.
You are working in this iOS project. Do not add features.
Goal: Produce a list of personal data the app collects so I can fill the App Privacy label. Do not change product behaviour.
The project may be Expo, React Native, Swift, a Capacitor wrapper, or a FlutterFlow export. Change only files that already exist.
Steps:
1. Search for analytics, crash reporting, auth fields, photo uploads, and requests to model APIs.
2. Write a table of data type, where it is sent, and whether it is linked to the user. Use only what you can see in the repo.
3. Do not tell me to tick 'data not collected' if you found a network call that sends user content.
4. Do not print certificates, private keys, API keys, or App Store Connect passwords.
5. Tell me which files you changed, and list anything I still have to do in Xcode or App Store Connect.
A subscription or other in-app purchase left in Missing Metadata cannot be reviewed. The product needs a reference name, a product id that the app requests, a price, a localisation, and a review screenshot. Status should be Ready to Submit before you send the version. Guideline 2.1(b) says configured purchases have to be visible to the reviewer and working.
You are working in this iOS project. Do not add features.
Goal: The app requests StoreKit product ids that I can create in App Store Connect. Do not invent prices.
The project may be Expo, React Native, Swift, a Capacitor wrapper, or a FlutterFlow export. Change only files that already exist.
Steps:
1. Find the product ids in the purchase code.
2. List them for me with a one-line description of what each unlocks.
3. If an id is still a sample such as premium_monthly from a tutorial, flag it and stop.
4. Do not print certificates, private keys, API keys, or App Store Connect passwords.
5. Tell me which files you changed, and list anything I still have to do in Xcode or App Store Connect.
A product can be Ready to Submit and still not travel with this binary. On the version page, the In-App Purchases and Subscriptions section is where you attach the first submission of a product. If it is not attached, the reviewer cannot see it, and 2.1(b) is the usual reply.
You are working in this iOS project. Do not add features.
Goal: Document which product ids must be attached to this version. Do not change purchase code unless an id is wrong.
The project may be Expo, React Native, Swift, a Capacitor wrapper, or a FlutterFlow export. Change only files that already exist.
Steps:
1. List the product ids referenced by the release build.
2. Note which ones are new and therefore need to be attached on the version page.
3. Do not remove a product id the paywall still shows.
4. Do not print certificates, private keys, API keys, or App Store Connect passwords.
5. Tell me which files you changed, and list anything I still have to do in Xcode or App Store Connect.
App Store Connect rejects an upload whose build number was already used for that version. The version you show people (CFBundleShortVersionString) and the build number (CFBundleVersion) are different. Expo and FlutterFlow often leave both at 1.0.0 and 1, so the second archive fails.
You are working in this iOS project. Do not add features.
Goal: Version and build numbers are explicit, and the build number is not stuck at 1.
The project may be Expo, React Native, Swift, a Capacitor wrapper, or a FlutterFlow export. Change only files that already exist.
Steps:
1. Find CFBundleShortVersionString, CFBundleVersion, and the Expo or Flutter equivalents.
2. If the build number is still the template default, tell me the current values and where to bump them. Do not pick a store version I have not approved.
3. Keep the two numbers distinct in any config comment you add.
4. Do not print certificates, private keys, API keys, or App Store Connect passwords.
5. Tell me which files you changed, and list anything I still have to do in Xcode or App Store Connect.
Critical
A simulator build cannot be submitted. You need an archive of a generic iOS device, then Validate in the Organizer or via the upload tool, before you send it to review. Validation catches the missing purpose string, the wrong icon, and bitcode or signature problems while you can still fix them the same day.
You are working in this iOS project. Do not add features.
Goal: The iOS scheme can archive a release build for a generic device.
The project may be Expo, React Native, Swift, a Capacitor wrapper, or a FlutterFlow export. Change only files that already exist.
Steps:
1. Check the Release configuration is the one the Archive action uses.
2. Remove settings that only succeed for a simulator, such as an x86-only dependency, if you can do that without changing features.
3. Tell me the exact Xcode or EAS command to archive. Do not upload it.
4. Do not print certificates, private keys, API keys, or App Store Connect passwords.
5. Tell me which files you changed, and list anything I still have to do in Xcode or App Store Connect.
The name, subtitle and keyword field are how people find the app in search. A generated name that repeats the template, or keywords stuffed with competitors, gets edited back or rejected under the metadata rules. Guideline 2.3 says the metadata should describe the app people actually get. You have 30 characters for the name, 30 for the subtitle, and 100 for keywords, separated by commas without spaces.
You are working in this iOS project. Do not add features.
Goal: Draft an App Store name, subtitle and keyword field from the features that exist in the app.
The project may be Expo, React Native, Swift, a Capacitor wrapper, or a FlutterFlow export. Change only files that already exist.
Steps:
1. Read the in-app screens and list the features you can see. Do not add features from comments or TODOs.
2. Propose a name and a subtitle, each at most 30 characters, and a keyword list at most 100 characters, comma-separated, with no spaces after commas.
3. Do not include other companies' trademarks. Do not invent user counts or awards.
4. Do not print certificates, private keys, API keys, or App Store Connect passwords.
5. Tell me which files you changed, and list anything I still have to do in Xcode or App Store Connect.
Guideline 2.3.3 says screenshots should show the app in use, not only the title art, the login page, or a splash screen. App Store Connect also requires specific sizes for the devices you support. A stretched phone screenshot, or a picture of your website, is a metadata rejection. Capture them from the build you are submitting.
You are working in this iOS project. Do not add features.
Goal: List the screens I should capture for the App Store screenshots. Do not generate fake store images.
The project may be Expo, React Native, Swift, a Capacitor wrapper, or a FlutterFlow export. Change only files that already exist.
Steps:
1. Name up to six screens that show the app doing something, in the order a new person would see them.
2. Skip the splash screen and a bare login form unless login is the product.
3. Do not add badges, awards, or review quotes to the captions.
4. Do not print certificates, private keys, API keys, or App Store Connect passwords.
5. Tell me which files you changed, and list anything I still have to do in Xcode or App Store Connect.