So you have turned a bright idea into a release-ready iOS app, time to test it properly by friends and family on their physical phones!
This 3-part article will explain how we can go from an app created on our PC/laptop to the Apple App Store, via an app called TestFlight which enables external testers to use the preview version of our app.
The analogy (from this article)
- We (the developers): The brand owners
- Our app: The product
- EAS (Expo Application Service): The manufacturer + logistics company
- Apple App Store: The supermarket
- TestFlight: supermarket's tasting room. The product (our app) must be "registered" first before it can be entered in this room
Part 1 โ Identity & Setup
This part connects your local project, Expo, and Apple so they all agree on your appโs identity.
Step 1/7 : Install and log in to EAS
npm i -g eas-cli
eas login
Step 2/7 : Initialize EAS in your project
eas init
This links your repo to an Expo project.
Step 3/7 : Verify app.json (or app.config.*)
Make sure these two things are correct:
-
ios.bundleIdentifierThis must match what youโll use in App Store Connect.
"expo": {
"name": "oauth-pro2",
"ios": {
"bundleIdentifier": "com.cathyapp1234.oauthpro2",
},
}
-
extra.eas.projectIdThis links your local project to Expo EAS.
"expo": {
"extra" : {
"eas": {
"projectId": "a31c0453-d16e-4818-aa19-d380a5bfc5d4"
}
}
}
Step 4/7: Define build Profiles in eas.json
Regardless of a build profile (eg "preview" or "production"), there are two types of builds:
- Ad Hoc Build, binary is on Expo :
"distribution": "internal"
- Signed and is permitted to be on the App Store
"distribution": "store"
We are building the latter so we can download it via TestFlight.
Also, it is useful to auto increment the build number with every EAS build
"autoIncrement": true,
The eas.json:
{
"build": {
"preview": {
"distribution": "store",
"channel": "preview",
"autoIncrement": true,
},
}
}
The convention is to use EAS remote build number instead of keeping tracks of it ourselves:
{
"cli": {
"appVersionSource": "remote"
}
},
In this system, build number is a single counter tied to your Project ID and Platform (iOS), not to a specific build profile. This means, both
eas build --profile preview
and
eas build --profile production
will both increase the same build number.
Step 5/7: Add the Corresponding submit Profiles
Make sure the entries are available, even if empty. Otherwise the build will give errors.
{
"submit": {
"production": {},
"preview": {}
}
}
Final eas.json can be found in this GitHub repository.
Step 6/7: Channels and OTA (Over the Air Updates)
Since we have specify different channels for each build profile, we can send code updates (bug fixes, small changes) over the air (OTA) via the channels to phones that already have the app installed, without having to resubmit to the App Store.
You can choose to skip this step, but EAS will give a warning.
Run the followings for this function
npx expo install expo-updates
eas update:configure
Step 7/7: Check into remote GitHub
git push origin main
Next, we are ready to build our app!


Top comments (2)
This is a really clear breakdown โ the supermarket/TestFlight analogy makes a confusing process click instantly ๐
I like how you focus on identity + setup first; thatโs exactly where most Expo/EAS issues come from. Looking forward to Part 2, especially the TestFlight side of things.
Thanks Bhavin!
It took me many Q n A sessions with ChatGPT and Gemini and practices before I can understand everything. Hard to believe why everything is so confusing..!
Part 2/3 should be a bit easier now. ๐