Cloud agents + MobAI = iOS development
Claude Code on the web, Codex in the cloud and Cursor background agents run in Linux
sandboxes. No Xcode, no simulator, no phone, so they could write iOS code but never see
it run. MobAI 3.0 changes that with mobai-dev,
one binary installed by a sandbox setup script. Preview the app on Linux, build it in
your own CI, run it on a simulator, then install it on the iPhone on your desk and drive it.
Preview
FREEFlutter, React Native and SwiftUI apps rendered in a phone-sized viewport on the sandbox's own Linux machine.
Build
FREEiOS builds on your own macOS runners: GitHub Actions, Codemagic or Bitrise. One, or all three to stack their free minutes.
Simulator
PROThe same runner boots a simulator, installs the build and hands the agent the controls.
Your own device
FREE ALLOWANCE THEN PROInstall and test on your own iPhone over your Tailscale network, with your computer off.
What you need
For previews, builds and simulators: nothing but the setup command below. No account to create, no API key, no Tailscale, no Mac. When the agent signs in with your email, the account is created on the spot.
For your own phone, and for over-the-air installs on it:
- The MobAI desktop app on a Mac, for the one-time phone setup. Download.
- An iPhone on iOS 17 or later, connected by cable during that setup.
- Tailscale on the phone, connected to the same tailnet the sandbox joins.
Set up your sandbox
One setup command for every platform. The script installs mobai-dev,
Tailscale, and the skills that teach the agent every command on this page. It reads
the environment first, and for whatever is missing the agent asks you in the session:
an emailed sign-in code, a Tailscale login link. The binary
lands in ~/.mobai/bin; the agent uses the full path when that
directory is not on its command path.
Set the sandbox's network access to full internet. No host allowlist covers what a build needs: GitHub, your CI provider, npm, pub.dev, CocoaPods, Apple's servers and Tailscale.
The desktop app shows the same setup, with your account email and API key filled in: Integrations > Cloud Agents, or the last step of the phone wizard.
Claude Code
In the repository settings, set the environment setup command to:
curl -fsSL https://mobai.run/cloud/install.sh | sh
Set this plain environment variable:
MOBAI_ACCOUNT_EMAIL=<your mobai account email>
Claude Code's environment variables are visible to everyone using the environment, and they are absent while the setup command runs. Do not put API keys, Tailscale keys or signing credentials there. The script installs tools only. In the chat, the agent signs you in with an emailed code; for phone access it also gives you a Tailscale login link to approve.
Network access: Claude Code filters outbound traffic by default. Set the environment's network access to full internet.
Cursor
In .cursor/environment.json, set the install command to:
curl -fsSL https://mobai.run/cloud/install.sh | sh
Add these values to the environment's secrets:
MOBAI_API_KEY=<your mobai API key>
MOBAI_TAILSCALE_KEY=<tailscale ephemeral auth key>
MOBAI_ACCOUNT_EMAIL=<your mobai account email>
The email identifies your account, the API key authenticates the sandbox, and the Tailscale key is only needed for your phone. Without an API key the agent signs in with an emailed code; without a Tailscale key it gives you a login link to approve when phone access is wanted.
Network access: Cursor allows all traffic by default. Keep it that way. If you turned filtering on, turn it off for this environment.
Codex
In the environment settings, set the setup script to:
curl -fsSL https://mobai.run/cloud/install.sh | sh
Add these values to the environment's secrets:
MOBAI_API_KEY=<your mobai API key>
MOBAI_ACCOUNT_EMAIL=<your mobai account email>
The API key is optional if you sign in during the session with an emailed code.
MOBAI_TAILSCALE_KEY is accepted the same way as on Cursor.
Your phone is not reachable from Codex yet. Its sandbox blocks the connection a tailnet join needs, and a Tailscale key does not change that. The script installs Tailscale and tries anyway, so nothing needs re-pasting the day Codex allows it, and the agent is told why the join fails. Previews, builds and simulators work. Use Claude Code or Cursor when the phone is the point.
Network access: Codex filters outbound traffic by default. Set the environment's internet access to full. The tailnet restriction is separate from that setting.
Grok bot
Grok has no setup command field. Paste the same setup command into the chat and the bot runs it in its sandbox:
curl -fsSL https://mobai.run/cloud/install.sh | sh
No environment variables needed. The agent signs in from the chat with an emailed code.
First session
Open a session in your project and say:
Set up the MobAI preview for this project and show me the main screen.
The agent runs the project setup, installs any missing preview engine, starts the preview and comes back with a screenshot. If a sign-in is needed it asks for an emailed code:
mobai-dev login --email [email protected]
mobai-dev login --email [email protected] --code <code>
Signing in is free and creates the account when the email is new. From there, ask for the next thing:
- Show the paywall in dark mode.
- Test onboarding with location permission denied.
- Build the iOS app.
- Run this version on a simulator.
- Install it on my phone and tap through onboarding.
1. Preview FREE
Flutter, React Native and even SwiftUI apps render in a phone-sized viewport on the sandbox's own Linux machine. The preview runs your app's code without a device, a simulator or a Mac.
Run setup once per project, naming the framework and the agent:
mobai-dev setup --agent claude --framework <flutter|react-native|swiftui>
# --agent cursor or --agent codex on those platforms
Setup writes .mobai/config.json, installs the skill that teaches
the agent the preview workflow, and prints the commands for anything the machine still
needs. The agent follows that plan, then starts the preview:
mobai-dev preview run
The agent can:
- Read the screen as a semantic tree.
- Tap and type by label, scroll, take screenshots.
- Hot reload after edits.
- Mock location, permissions, the camera, network responses and the signed-in user.
- Exercise loading, empty, error and denied-permission states.
Packages that need real hardware are repaired with small adapters; a catalogue of ready-made ones ships with the engines. Use the preview for UI iteration, then verify native behaviour and performance on a simulator or your phone.
2. Build FREE
iOS builds run on a macOS runner in your own CI. GitHub Actions is the default; Codemagic and Bitrise are one flag away. Set a project up for GitHub Actions and build:
mobai-dev build init
mobai-dev build --ios
Or on a configured provider:
mobai-dev build --ios --provider codemagic
mobai-dev build --ios --provider bitrise
The command pushes a snapshot of the working tree, uncommitted changes included, to your
GitHub repository. The selected provider builds that snapshot, and the agent gets the
artifact, normally under ./build/. Without
--provider the command uses the provider in
builder.json, then GitHub Actions.
Use one provider, or set up all three and stack the free minutes each gives you. Builds are free in MobAI; runner time belongs to your CI account. The setup per provider is in CI providers.
Android builds run in the sandbox itself when the Android toolchain is present:
mobai-dev build --android.
3. Simulator PRO
Start a simulator running the project:
mobai-dev sim start
mobai-dev sim start --provider codemagic
mobai-dev sim start --duration 30m
The CI runner builds the working tree, boots an iOS simulator, installs the build and publishes the simulator to your MobAI account. The agent receives a device id and takes it from there: live screen, taps, typing, flows, tests. You can watch and take over from the MobAI desktop app.
The provider needs MOBAI_API_KEY in its secret store to publish
the simulator. The default idle window is 30 minutes; Codemagic and Bitrise cap it at 60.
An unused simulator ends when its window runs out, or earlier with:
mobai-dev sim stop
4. Your own device FREE ALLOWANCE THEN PRO
After the one-time phone setup, the agent installs a signed build on the iPhone on your desk and drives it from the sandbox over your own tailnet. Your Mac can be off. Before device work the agent brings the session up:
~/.mobai/bin/mobai-up
It handles the MobAI sign-in, joins the tailnet and starts device discovery. A reachable phone appears in the device list within about a minute:
mobai devices
mobai app install ./build/App.ipa
mobai bridge start
mobai observe
mobai screenshot --path /tmp
The agent sees the phone's UI tree and screenshots. It launches apps, taps, types, scrolls and runs complete test flows, as if the phone were plugged into the sandbox.
Install over the air, without the bridge
A signed build can also go onto the phone with nothing running on it:
mobai-dev ota App.ipa
mobai-dev ota stop
The command checks the build's provisioning profile against your onboarded phones before
serving anything, then serves the build and an itms-services
install link over your tailnet. Open the link on the iPhone and tap Install. If Tailscale
Serve approval or HTTPS certificates are missing, the command names the setting and the
next step. This installs the app only; driving it still needs the bridge.
CI providers
Project setup is local and makes no network request, so you or the agent can run it from
the sandbox. mobai-dev build init
writes builder.json and the provider's workflow files, never
overwrites a file that exists, and lists what remains: commit and push, create and connect
the provider app if needed, supply the token, add secrets. The first provider you set up
becomes the project default; --set-default moves it.
GitHub Actions
Nothing to create. The workflows run in the repository's own Actions on GitHub's macOS runners.
mobai-dev build init
# commit and push: builder.json, .github/workflows/ios-build.yml, .github/workflows/ios-share.yml
GitHub runs the copy of a workflow that is on the default branch, so the files must be
merged there before the first build. Secrets go under the repository's
Settings > Secrets. The sandbox needs a GitHub credential
that can push the snapshot and use the Actions API: GITHUB_TOKEN,
a gh login, or the sandbox's injected credentials.
Codemagic
In Codemagic, add an application, connect it to this GitHub repository and pick YAML
configuration. The app id is the value after /app/ in its URL.
mobai-dev build init --provider codemagic --app-id <id> --branch <branch>
# commit and push to <branch>: builder.json, codemagic.yaml, .builder/ci/runner.sh
Create an API token under Account settings and give it to the sandbox as
CODEMAGIC_API_TOKEN. In the app's environment variables, create
a group named builder: the runner's secrets go there, marked
Secret, plus the non-secret BUILDER=1 used for unsigned builds.
Bitrise
In Bitrise, create a CI project connected to this GitHub repository and choose a macOS Xcode
stack. The slug is the value after /app/ in the project URL.
mobai-dev build init --provider bitrise --app-id <slug> --branch <branch>
# commit and push to <branch>: builder.json, bitrise.yml, .builder/ci/runner.sh
Create a personal access token under Account settings > Security and give it to the
sandbox as BITRISE_API_TOKEN. The runner's secrets go in the
project's Secrets tab, not in bitrise.yml.
Tokens, secrets and branches
Codemagic and Bitrise read their workflow file from the branch you named at setup; the source snapshot never carries it. Both still need the sandbox's GitHub credential to push the snapshot. Keep provider tokens in the platform's secret store; Claude Code's plain environment variables are not a place for them.
The runner reads these secrets, exact names, in every provider's own store:
| Secret | Value | Needed for |
|---|---|---|
| IOS_CERTIFICATE | Base64 of your signing .p12 | Signed builds |
| IOS_CERTIFICATE_PASSWORD | The .p12 password, plain text | Signed builds |
| IOS_PROVISIONING_PROFILE | Base64 of your .mobileprovision | Signed builds |
| MOBAI_API_KEY | Your MobAI API key, plain text | Shared simulators |
An unsigned build needs none of the three signing secrets. A phone needs a signed build
whose profile includes that phone. Signing is switched on by ios.signing
in builder.json.
What the agent sees when something is missing
With --json every failure is one envelope with a code and a
suggestion naming the next step:
| Code | Meaning | Fix |
|---|---|---|
| CONFIG_INVALID | The provider has no app id or branch in builder.json. | Create and connect the app, then run build init with --provider, --app-id and --branch. |
| AUTH_REQUIRED | The provider token is missing, or the GitHub credential cannot use the Actions API. | Set CODEMAGIC_API_TOKEN or BITRISE_API_TOKEN. For GitHub, use a credential with Actions access. |
| CONFIG_NOT_FOUND | The workflow files were missing; they have just been written. | Commit and push them to the branch the envelope names. |
| BUILD_FAILED | The provider rejected the request, did not confirm it, or the run failed. | Rejected: check app id, branch and token. Unconfirmed: check the dashboard before retrying, a job may be running. Failed: open the run URL in the envelope. |
Your phone
Connect the iPhone to your Mac by cable. In the MobAI desktop app, hover its row in the device list and press the cloud button. The wizard has five steps.
- Pair for cloud. A separate pairing identity for cloud access, stored encrypted in your account. Unlock the phone and confirm Trust if asked. Local pairing stays separate, so revoking cloud access never breaks your local setup. An already onboarded phone can jump straight to any step.
- Prepare the device. The bridge is installed on the phone. Keep the cable connected and the phone unlocked; this can take a few minutes. A cloud session uses the bridge already on the phone.
- Tailscale. Install Tailscale on the iPhone, sign in and leave it connected. Same tailnet as the sandbox, or the phone never appears.
- API key. The key the sandbox uses for your account and your devices. Revoke it any time from API Keys. Claude Code signs in from the chat instead and does not need it.
- Set up the agent. Pick your platform and copy its setup command and environment values. The build project setup and the optional signing steps are here too.
After that, disconnect the cable. Keep the phone awake and on Tailscale when the agent needs it.
The same agent setup without a phone lives under Integrations > Cloud Agents in the desktop app, for previews, builds and simulators.
Signing
A physical iPhone needs a signed build whose provisioning profile includes that phone's UDID. Signing also decides how long the bridge on the phone stays usable:
- Paid Apple Developer account: a year.
- Free Apple ID: seven days. Reconnect the phone and repeat step 2 of the wizard.
The wizard shows how to produce the two files:
- Register the phone in the Apple developer portal.
- Create a wildcard provisioning profile (App ID
*) that includes the phone. Wildcard matters: it has to cover both your app and the bridge. - In Keychain Access, export your Apple Development certificate as a .p12.
- Base64 both, then paste them as the secrets above.
base64 -i profile.mobileprovision | tr -d '\n' | pbcopy
base64 -i identity.p12 | tr -d '\n' | pbcopy
For CI builds those are IOS_CERTIFICATE,
IOS_CERTIFICATE_PASSWORD and IOS_PROVISIONING_PROFILE
in the provider. For signing inside a phone session, the wizard also offers:
MOBAI_SIGN_PROFILE_B64=<base64 of your .mobileprovision>
MOBAI_SIGN_P12_B64=<base64 of your .p12>
MOBAI_SIGN_P12_PASSWORD=<the p12 password>
With these set and a paid Apple Developer account, sessions keep the phone ready on their own and sign the apps they build. That needs a platform with a secret store and phone access, which today means Cursor. On Claude Code, keep the signing secrets in CI and let the build come out signed there.
Limits
- Previews and builds are free in MobAI. CI minutes count against your provider's allowance.
- Shared simulators are free too. Your phone comes with a metered free allowance; sustained use needs Pro.
- Codex cannot reach a phone yet. Previews, builds and simulators work there.
- iOS 17 or later on the phone, awake and on the same tailnet as the sandbox.
- One session per phone. Two sandboxes driving the same phone fight over it.
Troubleshooting
The agent says there are no devices
An empty list is not an error. The phone may be asleep, off the tailnet or still connecting. Wake it, confirm Tailscale is connected and on the same tailnet as the sandbox; a reachable phone appears within about a minute of the session starting. If it still does not, reconnect the phone to Wi-Fi, toggle Tailscale off and on, and bring the MobAI app on the phone to the front.
Ask the agent to read ~/.mobai/mobai.log. It records which
peers were found on the tailnet, what each connection attempt failed on and when it retries.
A phone can show as online in Tailscale and still refuse the connection MobAI needs.
On Codex the tailnet join is blocked by the sandbox; retries will not fix it. Continue with a preview or a simulator, or use Claude Code or Cursor for the phone.
The setup script fails on a missing variable
Use the exact command for your platform from this page. Claude Code's script receives no
environment variables and needs none; a script that demands them there is the wrong script
or an old copy. On Cursor and Codex, check the values sit in the environment's secrets. Without
MOBAI_API_KEY, finish the emailed sign-in in the session instead.
Downloads fail partway through
Set network access to full internet and run the setup command again. Release downloads redirect, and package installs reach more hosts than any list anticipates. On Cursor, check whether you turned filtering on; on Claude Code and Codex it is on by default.
GitHub Actions cannot be reached
If the agent reports that its GitHub credential cannot use Actions, fix the credential. If the sandbox itself blocks the GitHub API, no token helps: run the workflow from GitHub's web interface, or build on Codemagic or Bitrise, which use their own APIs and only need a git push.
The app will not install on the phone
The build is unsigned, or its provisioning profile does not include the phone's UDID. Set
the three signing secrets in the provider and rebuild, or register the phone in the Apple
developer portal, refresh the profile and update the secret.
mobai-dev ota
checks both before it serves anything.
It worked yesterday and now it does not
With a free Apple ID the bridge's signing expires after seven days. Connect the phone to your Mac and repeat step 2 of the wizard, Prepare the device.
What is shared
Previews run in your sandbox. Builds and simulators run in your own CI account. Phone traffic goes between the sandbox and your iPhone over your own tailnet. MobAI hosts nothing in that data path.
For each phone you onboard we store its identifier, name, iOS version and an encrypted pairing record, so your sessions can find and connect to it.
Remove a phone from the cloud setup wizard and everything stored for it is deleted. The API key is revoked separately, from API Keys. The binary the sandbox runs is open source: github.com/MobAI-App/mobai-dev.