# EAS Submit iOS: publish an Expo app to the App Store

> EAS Submit uploads your iOS build to App Store Connect, where it lands in TestFlight. You still submit it for review yourself in App Store Connect.

Original page: [EAS Submit iOS: publish an Expo app to the App Store](https://devoria.fr/en/blog/eas-submit-ios-publish-expo-app-store)
Author: [Mehdi Azizi](https://devoria.fr/en/mehdi-azizi)
Published: 2026-10-25
Content last revised: 2026-10-25


`eas submit --platform ios` uploads your iOS build to App Store Connect, where it shows up in TestFlight once Apple has processed it. **The command doesn't submit your app to Apple's review: you still pick the build and submit it for review in App Store Connect.** To automate it, it needs your app record's number (`ascAppId`) and an App Store Connect API key.

I publish my iOS apps, including Solys, TradaZen and Nolera, with Expo, EAS Build and EAS Submit. This guide follows [Expo's documentation on iOS submission](https://docs.expo.dev/submit/ios/), reviewed on October 25, 2026. If you don't have a Mac, the full walkthrough is in [publish an iOS app without a Mac](https://devoria.fr/en/blog/publish-ios-app-without-mac-expo-eas). Here, we focus on the upload.

## What you need before the first command

Expo lists three prerequisites:

- **A paid Apple Developer account.** Price and sign-up are covered in [Apple developer account](https://devoria.fr/en/blog/apple-developer-account-cost-free-or-99).
- **A bundle identifier** in `ios.bundleIdentifier`. According to [the Expo config reference](https://docs.expo.dev/versions/latest/config/app/), you make it up, but it has to be unique on the App Store.
- **EAS CLI installed**, then `eas login` with your Expo account.

You also need a signed production build. EAS Build creates it with the certificate and profile it manages for you, as explained in iOS certificate and provisioning profile.

### The App Store Connect record: you or EAS?

EAS Submit uploads a binary to an app that has to exist in App Store Connect.

**You can create it yourself.** [Apple's help](https://developer.apple.com/help/app-store-connect/create-an-app-record/add-a-new-app) describes the New App window: platform, name, primary language, bundle ID, SKU and user access. You need the Account Holder, Admin or App Manager role.

**EAS can also create it on the first upload.** According to [Expo's TestFlight page](https://docs.expo.dev/submit/testflight/), `eas submit` creates the record when you upload your first build. Per [the eas.json reference](https://docs.expo.dev/eas/json/), setting `ascAppId` skips this step. The name comes from `appName`, or if that's missing, from the `name` in your config. The primary language comes from `language`, which defaults to `en-US`. **If your app launches in French first, set `language` before that first upload.**

Then copy the record's number into `ascAppId`. Expo tells you where to find it: open your app in App Store Connect, then App Information. It's the Apple ID field, under General Information. **It's a number, not your bundle identifier.**

## Why an API key instead of your Apple ID

EAS Submit connects to Apple with your Apple ID or with an App Store Connect API key.

The Apple ID runs into two-factor authentication, which [Apple requires](https://developer.apple.com/support/authentication/) for every App Store Connect user: someone has to confirm the sign-in. An app-specific password, passed in `EXPO_APPLE_APP_SPECIFIC_PASSWORD`, skips that step, but [Expo considers it less secure](https://github.com/expo/fyi/blob/main/apple-app-specific-password.md) than the API key, its default and recommended method. **The key lets you authenticate with zero interaction, so you can automate the upload.**

Two ways to get one:

- **Let EAS manage it.** Run `eas credentials --platform ios`, pick the `production` profile, sign in to your Apple account, then choose "App Store Connect: Manage your API Key".
- **Bring your own key.** In App Store Connect: Users and Access, Integrations tab, App Store Connect API, Team Keys, then Generate API Key. You need an Admin account, and [Expo's guide](https://github.com/expo/fyi/blob/main/creating-asc-api-key.md) gives the key the Admin role.

In the second case, write down the Key ID, shown in the list of active keys, and the Issuer ID, [at the top of the Integrations page](https://developer.apple.com/documentation/appstoreconnectapi/generating-tokens-for-api-requests). Above all, keep the `.p8` file: [you can only download it once](https://developer.apple.com/documentation/appstoreconnectapi/creating-api-keys-for-app-store-connect-api), and Apple doesn't keep a copy.

> According to Apple, an Admin key can create and delete users on your account. Apple says never to store the key in a code repository.

## The minimal eas.json to copy

All values are fake: replace them with yours.

```json
{
  "cli": {
    "appVersionSource": "remote"
  },
  "build": {
    "production": {
      "autoIncrement": true
    }
  },
  "submit": {
    "production": {
      "ios": {
        "ascAppId": "1234567890",
        "appleTeamId": "ABCDE12345",
        "ascApiKeyPath": "./keys/AuthKey_XXXXXXXXXX.p8",
        "ascApiKeyIssuerId": "your-issuer-id",
        "ascApiKeyId": "XXXXXXXXXX"
      }
    }
  }
}
```

- **`appVersionSource` and `autoIncrement`**: EAS servers store the build number and bump it with every production build. [Expo recommends this remote mode](https://docs.expo.dev/build-reference/app-versions/) since EAS CLI 12.
- **`ascAppId`**: your app record's number. Without it, non-interactive mode fails.
- **`appleTeamId`**: your Apple Developer team ID.
- **`ascApiKeyPath`, `ascApiKeyIssuerId`, `ascApiKeyId`**: your API key. All three fields accept environment variables. Add the `keys` folder to your `.gitignore`.

The `appleId` field only matters if you sign in with your Apple ID. If EAS already manages your key, also remove the three `ascApiKey` fields. On my three apps, checked on October 25, the production submit profile contains only `ascAppId`, plus `appleTeamId` on Solys: no key in the file.

## Run the build, then the upload

```bash
eas build --platform ios --profile production
eas submit --platform ios
```

With no other option, `eas submit` asks you to pick the build to upload and, on the first run, asks for your Apple ID. The `production` submit profile is used by default if it exists. The [EAS CLI reference](https://docs.expo.dev/eas/cli/) lists the options that skip the picker:

| Option           | What it does                 |
| ---------------- | ---------------------------- |
| `--latest`       | Uploads the latest iOS build |
| `--id`           | Uploads a specific EAS build |
| `--path`         | Uploads a local .ipa file    |
| `--url`          | Uploads an online archive    |
| `--groups`       | Adds to an internal group    |
| `--what-to-test` | Fills in "What to Test"      |

To chain everything, add `--auto-submit` to the build command. According to [Expo's page on automatic submissions](https://docs.expo.dev/build/automate-submissions/), the finished build goes to EAS Submit with the submit profile that has the same name as the build profile. To pick another one: `--auto-submit-with-profile=name`. **The build goes to TestFlight, not to review.**

In continuous integration, add `--non-interactive` and an `EXPO_TOKEN` token. The command then can't ask you anything: it needs `ascAppId` in the profile and an API key, either the one EAS attached to the app or yours in the three `ascApiKey` fields.

## What happens after the upload

**1. Apple processes the build.** It has to go through processing before it shows up in App Store Connect, and [Apple sends you an email](https://developer.apple.com/help/app-store-connect/manage-builds/upload-builds) when it's done. Expo usually mentions 10 to 15 minutes before you see it in TestFlight.

**2. Export compliance.** The Missing Compliance status means your encryption declaration is missing. App Store Connect asks these questions for every new version, unless the Info.plist already answers them. With Expo, the key is `ios.config.usesNonExemptEncryption`:

```json
{
  "expo": {
    "ios": {
      "config": {
        "usesNonExemptEncryption": false
      }
    }
  }
}
```

According to [Apple](https://developer.apple.com/documentation/security/complying-with-encryption-export-regulations), `false` is fine if your app, libraries included, uses no encryption or only exempt encryption. System encryption, like HTTPS through URLSession, usually is. **Only declare `false` if it's true for your app: you're accountable for the declaration.** Like any Info.plist key, it requires a new build.

**3. TestFlight.** At the Ready to Submit status, [according to Apple](https://developer.apple.com/help/app-store-connect/reference/app-build-statuses), the build can go to internal testers, go through beta review for external ones, or go to App Store review. The `groups` field in the profile adds it to your internal groups. Everything else is in [TestFlight to test your app](https://devoria.fr/en/blog/testflight-test-iphone-app-before-submitting).

## Uploading isn't submitting for review

[Expo says it plainly](https://docs.expo.dev/submit/introduction/): a TestFlight build isn't published to the App Store automatically. EAS Submit doesn't fill in your listing or your screenshots. You still have four steps in App Store Connect, described in [Apple's help on submitting for review](https://developer.apple.com/help/app-store-connect/manage-submissions-to-app-review/submit-an-app):

1. **Fill in the required metadata** for the version.
2. **Pick the build** in the version's Build section.
3. **Click "Add for Review".** The version moves to Ready for Review, but nothing has gone to review yet.
4. **Click "Submit for Review".** The status changes to In Review when the review starts.

## Common errors and how to fix them

| Error shown         | Cause                     | Fix                       |
| ------------------- | ------------------------- | ------------------------- |
| ITMS-90189          | Build number already used | `autoIncrement`           |
| ITMS-90062 or 90186 | Version already closed    | Bump `version`            |
| "Set ascAppId"      | `ascAppId` missing in CI  | Add `ascAppId`            |
| "API Keys cannot"   | No key attached           | `eas credentials`         |
| ITMS-90683          | Missing purpose string    | `ios.infoPlist`           |
| ITMS-91053          | Incomplete manifest       | `ios.privacyManifests`    |
| ITMS-90725          | iOS SDK too old           | Upgrade the Expo SDK      |
| Missing Compliance  | Encryption not declared   | `usesNonExemptEncryption` |

According to Expo, a build number that's already used is a common cause of upload rejection: it's all in build number and version. Three errors have their own guide: [ITMS-90683 with Expo](https://devoria.fr/en/blog/itms-90683-missing-purpose-string-expo), [ITMS-91053 with Expo](https://devoria.fr/en/blog/itms-91053-missing-api-declaration-expo) and ITMS-90725.

The two quoted messages come from EAS and get fixed in its config. Missing Compliance can also be fixed by answering the questions in App Store Connect. **For every other row, the fix touches the binary: rebuild, then upload again.** An over-the-air update doesn't change the Info.plist, the manifest or the numbers.

## Ship your first app without fumbling

The command is just one step. The [iOS Kit](https://devoria.fr/en/kits/ios) starts from the Starter Code of my apps, subscriptions included, and walks you through four steps all the way to the App Store, at your own pace.

If you're preparing your first launch, [take the free diagnostic](https://devoria.fr/en/diagnostic): it tells you what's really holding you back and sends you my Apple anti-rejection checklist. For the listing, screenshots and approval, the page [publish an app on the App Store](https://devoria.fr/en/expertise/publish-an-app-on-the-app-store) covers every step.

## Frequently asked questions

### How do I publish an Expo app to the App Store?

Run a production build with EAS Build, then upload it with `eas submit --platform ios`. After Apple processes it, it shows up in TestFlight. Then in App Store Connect, fill in the listing, pick that build in the version, and click "Add for Review" and "Submit for Review".

### Does EAS Submit send my app to Apple's review?

No. EAS Submit uploads the binary to App Store Connect, and the build lands in TestFlight. Submitting for review is done manually in App Store Connect, once the listing is filled in and the build is picked.

### Where do I find my app's ascAppId?

In App Store Connect, open your app, then App Information. It's the Apple ID field, under General Information. It's a number, not to be confused with your bundle identifier.

### Do I need an Apple ID to use eas submit?

On the first run, the command asks for it. After that, with `ascAppId` in the profile and an App Store Connect API key, it can run without interaction, even in continuous integration. The API key is Expo's default and recommended method.

### How long before I see the build in TestFlight?

Expo usually mentions 10 to 15 minutes of processing on Apple's side, and Apple emails you when it's done. If the build stays at Missing Compliance, answer the encryption questions, then declare it in your config for future builds.


## Your project

[Apple Review Kit](https://devoria.fr/en/kits/review-apple): The App Store review checklist I use, my Apple rejections and fixes, notes for the reviewer and two Claude Code skills that review your app. 29 €.

[iOS Kit](https://devoria.fr/en/kits/ios): Build your iPhone app with Claude Code: the starter code behind my apps, skills that check its work, and the Apple Review, Screenshots and ASO kits. 129,99 €.
