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.

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:

SecretValueNeeded for
IOS_CERTIFICATEBase64 of your signing .p12Signed builds
IOS_CERTIFICATE_PASSWORDThe .p12 password, plain textSigned builds
IOS_PROVISIONING_PROFILEBase64 of your .mobileprovisionSigned builds
MOBAI_API_KEYYour MobAI API key, plain textShared 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:

CodeMeaningFix
CONFIG_INVALIDThe 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_REQUIREDThe 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_FOUNDThe workflow files were missing; they have just been written.Commit and push them to the branch the envelope names.
BUILD_FAILEDThe 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.

  1. 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.
  2. 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.
  3. Tailscale. Install Tailscale on the iPhone, sign in and leave it connected. Same tailnet as the sandbox, or the phone never appears.
  4. 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.
  5. 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:

  1. Register the phone in the Apple developer portal.
  2. Create a wildcard provisioning profile (App ID *) that includes the phone. Wildcard matters: it has to cover both your app and the bridge.
  3. In Keychain Access, export your Apple Development certificate as a .p12.
  4. 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.