Guide
Ship an iPhone app with an AI coding agent
By Joe Burwasser · Updated October 2026
I wrote this playbook for myself and for the AI coding agents I work with. It's how I took Alpaca Scalper and Timely Sleep from code to apps installed on my iPhone, with over-the-air updates for every change after that.
Every step is marked by who does it. The agent handles the repetitive setup, and you handle anything that needs your identity: signing in, two-factor codes, and your phone. That split is the heart of the playbook.
What you'll need
- A Mac with Node.js and npm. You don't need Xcode; Expo builds the app in the cloud.
- A free Expo account.
- An Apple Developer Program membership ($99 a year). One membership covers as many apps as you like.
- Expo Go on your iPhone, for quick testing while you build.
- An AI coding agent, like Claude Code or Cursor.
One tip before you start: don't add eas-cli to your project's dependencies. Run it with npx eas-cli@latest instead. Expo's health check flags it as a dependency, and it can break cloud builds.
Who does what
The agent can do on its own:
- Edit
app.json,eas.json, andpackage.json - Link the app to an Expo project and set up over-the-air updates
- Install packages and run health checks
- Publish updates and check on builds, once you're signed in
You do:
- Sign in to Expo in your browser
- Sign in to Apple and enter two-factor codes
- Run the very first build yourself, in your own terminal
- Register your iPhone, pay for the Apple membership, and accept Apple's agreements
When a step needs you, ask the agent to give you the exact command and the answers to each prompt. That way you're never guessing.
1. Set up the app (agent)
Create the app, or start from one you already have:
npx create-expo-app@latest my-app
cd my-app
Fill in the key fields in app.json. The bundle identifier is your app's unique ID on iOS, usually a reversed domain plus the app name:
{
"expo": {
"name": "My App",
"slug": "my-app",
"version": "1.0.0",
"runtimeVersion": "1.0.0",
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.yourname.myapp",
"infoPlist": { "ITSAppUsesNonExemptEncryption": false }
},
"android": { "package": "com.yourname.myapp" }
}
}
ITSAppUsesNonExemptEncryption: falseskips an App Store Connect export-compliance question. It's accurate for apps that only use standard HTTPS.- Use a fixed string for
runtimeVersion. The policy-based form errors untilexpo-updatesis installed.
Add an eas.json with build profiles. preview is the one you'll use to install on your own phone:
{
"cli": { "version": ">= 16.0.0", "appVersionSource": "remote" },
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"ios": { "simulator": false },
"channel": "development"
},
"preview": {
"distribution": "internal",
"ios": { "simulator": false },
"android": { "buildType": "apk" },
"channel": "preview"
},
"production": { "autoIncrement": true, "channel": "production" }
},
"submit": { "production": {} }
}
And a few scripts in package.json, so the commands are easy to remember:
"scripts": {
"start": "expo start",
"eas:build:ios": "npx eas-cli@latest build --platform ios --profile preview",
"eas:update": "npx eas-cli@latest update --channel preview --platform ios",
"eas:build:ios:prod": "npx eas-cli@latest build --platform ios --profile production"
}
2. Link the project and turn on updates (agent)
Once you're signed in to Expo (step 3), the agent can link the app to an Expo project:
npx eas-cli@latest init --force --non-interactive
Then set up over-the-air updates:
npx expo install expo-updates
npx eas-cli@latest update:configure --platform all --non-interactive
Afterward, have the agent look over app.json for duplicated entries. The configure step has been known to repeat array values.
Finally, a health check. Everything should pass before you build:
npx expo-doctor
npx expo install --check # fix mismatches with: npx expo install --fix
npx tsc --noEmit # TypeScript projects
3. Sign in to Expo (you)
Check whether you're already signed in:
npx eas-cli@latest whoami
If not, sign in. With Google sign-in, a browser window opens:
npx eas-cli@latest login --sso
If an agent can't open a browser, you can create an access token on expo.dev and run commands with EXPO_TOKEN=<token>. Never commit the token.
4. Run the first build (you)
The first build for each app generates your Apple credentials, so it has to run in your own interactive terminal:
npm run eas:build:ios
You'll be walked through a few prompts:
- Log in to your Apple account? Yes. Enter your Apple ID and password. The password is saved to your Mac's Keychain, not sent to Expo.
- Two-factor code. Enter the code from your device or a text message.
- Generate a distribution certificate? Yes. This only happens once; later apps reuse it.
- Register devices? Yes. Choose Website, open the link on your iPhone in Safari, and install the profile (Settings → Profile Downloaded → Install). Don't choose "Current Machine"; that registers your Mac.
- Pick your iPhone for the provisioning profile.
- Push notifications? Yes only if your app uses
expo-notifications.
Builds take about 10–20 minutes. When it's done, open the build link on your iPhone and install it. If iOS won't open the app, turn on Settings → Privacy & Security → Developer Mode. The phone restarts.
5. Ship changes after that
JavaScript and asset changes don't need a new build. Publish an update:
npm run eas:update -- --message "what changed"
The app checks for updates when it launches. Force-quit and reopen it once or twice to pick it up.
Native changes need a full rebuild: adding or upgrading a package with native code, changing permissions, icons, or the splash screen in app.json, or upgrading the Expo SDK. When you rebuild, bump runtimeVersion (say, 1.0.0 to 1.1.0) so older installs never receive code that expects new native pieces.
Adding another iPhone or iPad:
npx eas-cli@latest device:create # choose Website, open the link on the new device
npm run eas:build:ios # rebuild so the new device is included
Going to TestFlight or the App Store later:
npm run eas:build:ios:prod
npx eas-cli@latest submit --platform ios
Troubleshooting
- "iTunes service key is empty." You're on an old
eas-cli. Usenpx eas-cli@latest. - "Your developer account needs to be updated." Your Apple Developer membership isn't active yet, or there are agreements to accept at developer.apple.com.
- "You have no team associated with your Apple account." Your membership is paid but not active yet. It can take up to about 48 hours.
- "Couldn't find any credentials suitable for internal distribution." The first build ran non-interactively. Run it once yourself in a terminal.
- The cloud build fails at "Install dependencies." Run
npx expo-doctor, removeeas-clifrom dependencies, and fix version mismatches withnpx expo install --fix. - The agent says
whoamiis "Forbidden." Some agent sandboxes block the network call. Re-run it with full permissions before assuming you're signed out. - Apple or Expo errors that mention maintenance. Check Apple's and Expo's status pages and try again later.
- Keychain errors during Apple sign-in. Put
EXPO_NO_KEYCHAIN=1in front of the command. - The app stops opening about a year later. The certificate or provisioning profile expired. Run an interactive build to regenerate them, then reinstall.
Make it reusable
Once this worked, I saved it as a Claude Code skill, a set of instructions my agent loads whenever I start a new app. Every new app now gets the same setup, and the agent already knows which steps to hand back to me. If you use an AI coding agent, turning a guide like this into a reusable skill or rules file is one of the easiest ways to make it reliable.