Docs · Mobile Apps

Submitting to the Stores

App Store and Play Store review prerequisites, signing, and listings.

Both stores require an account, signing, and a review before your app reaches real users. Neither process is instant the first time through; budget for it separately from development time.

iOS: Archive and App Store Connect

You need a Distribution certificate to Archive, and, surprisingly, automatic signing needs at least one registered device even for an App Store build, not just for local testing. On a brand-new team with zero devices registered, Product, Archive fails with an error about having no devices from which to generate a provisioning profile. The message is misleading: the real block is that automatic signing can't create any profile at all with zero devices on the account.

To fix it:

  1. Create an Apple Distribution certificate: Xcode, Settings, Accounts, your team, Manage Certificates, then add an Apple Distribution certificate.
  2. Register one device: plug in an iPhone, enable Developer Mode on iOS 16 and later (Settings, Privacy & Security, Developer Mode, on, then restart), let Xcode register it, and retry Signing & Capabilities. You can also add a device UDID manually at developer.apple.com under Devices.
  3. Then Product, Archive (with the destination set to Any iOS Device, not a simulator), Organizer, Distribute App, App Store Connect, Upload. The archive itself is signed with the Distribution certificate; no device is required for the actual distribution, registering one only unblocks Xcode's automatic signing flow.

If you'd rather not register a device, uncheck "Automatically manage signing" and create an App Store provisioning profile manually for your bundle id at developer.apple.com, then download and select it in Xcode.

Store submission needs an active Apple Developer account and passing App Review. If your app includes in-app purchases, the Paid Applications Agreement has to be active under App Store Connect, Business, or purchases fail in both sandbox and production even after your app is approved.

Android: signing and Play Console

Create an upload keystore and read it from a gitignored keystore.properties file, so signing credentials never end up in the repository:

keytool -genkeypair -v -keystore ~/keystores/upload.jks \
  -alias upload -keyalg RSA -keysize 2048 -validity 10000

keystore.properties (at the module root; gitignore this file and any .jks alongside it):

storeFile=/absolute/path/to/upload.jks
storePassword=...
keyAlias=upload
keyPassword=...

app/build.gradle.kts reads this file if it exists and configures a release signing config from it, guarded so CI and a fresh clone still build unsigned when the file is absent. On your first upload, accept Play App Signing: Google holds the actual app signing key, and your keystore is only the upload key you sign with locally.

A few things that catch people out:

  • Bump versionCode on every upload, to any track, including internal testing. Play rejects a reused code outright. versionName, the user-facing version string, doesn't need to change for every internal bump.
  • License testing avoids real charges. Internal-testing purchases are real money unless the tester's Google account is added under Play Console, Setup, License testing. Add your testers there first; their purchases then use a free test card and renew on an accelerated clock, which is what makes it practical to exercise the full purchase-to-entitlement-to-notification loop before launch.
  • Products can't be created until a build exists. Play won't let you create subscription products until you've uploaded a bundle that includes the Play Billing library to at least one track. Upload to Internal testing first, then create the products (matching the ids in InApp::PRODUCT_PLANS).

Next

You've now covered the full mobile picture. Return to Mobile Overview, or move on to Deploying with Kamal for the web side of shipping.