App Builder
Apps is the first section in the MobAI desktop app. It keeps your iOS and Android apps in one place, runs them on simulators and phones, builds and signs them, uploads them to TestFlight and Google Play, and lets your coding agent do all of it with you.
Overview
Each app in MobAI is a folder on your computer. Open one and you get its page: what's going on now, what's next to set up, its builds, agent chats, tests and pull requests.
You need:
- The MobAI desktop app on macOS, Windows or Linux.
- MobAI Pro. You can try all of Pro for 7 days, no credit card needed. Start the trial from the upgrade dialog in the app (you sign in first). Without Pro you still see every screen, and actions that need Pro open the upgrade dialog.
- For iPhone apps on a Mac: Xcode. Without Xcode, or on Windows and Linux, iPhone builds run on GitHub Actions and you need a GitHub account.
- For Android apps: the Android SDK and a JDK. MobAI uses Android Studio's JDK when the system has none.
When a build needs a tool you don't have, MobAI names it first instead of failing halfway.
Add an app
Press New app on the Apps screen. There are three ways in:
- A folder. Pick the folder of a Flutter, React Native, Expo, Xcode or Android Gradle project. A folder inside a monorepo works too.
- Clone from GitHub. Connect GitHub inside the sheet, pick a repository, and MobAI clones it.
- New app. Start an Expo, Flutter, SwiftUI or Kotlin app. Flutter and Expo use their own create commands. MobAI then makes a git repository and asks before the first commit.
When an app is added, MobAI:
- works out the framework and its iOS and Android targets,
- writes build commands into
mobai.jsonfor whatever is missing, and asks before committing them, - adds MobAI skills for agents to
.claude/skillsand.agents/skills.
A folder that isn't on GitHub yet can go there with Put on GitHub. MobAI shows what goes up, asks for the repository's owner, name and visibility (private by default), makes it and pushes.
Agents
MobAI starts the agent you pick, in your app's folder, with your own account: Claude Code, Codex, Cursor, Grok Build or OpenCode. It never installs one. For an agent this computer doesn't have, the picker links to its install page, with Check again once you've installed it.
Type what you want in the box on the app's page. The chat opens next to the device the agent uses, so you can watch it work and tap the device yourself. In the chat:
- Read-only tools and MobAI's own tools run without asking. Any other tool asks you first, one question at a time.
- Add device in the header gives the session phones and simulators to use. Free hands one back.
- Pick an element on the device to point the agent at it.
- Every check the agent runs with
verify_changeskeeps its screenshots. Open them from "N checks". - Follow-ups go on in the same agent conversation. Delete removes the chat. Commits, pull requests and builds stay.
Each agent offers its own models. Claude Code also has Ultrathink and Ultracode. The last pick is kept per agent.
Claude Code runs in its auto permission mode and Codex keeps its workspace sandbox. MobAI never turns their permission checks off.
Run on device
Run on device sits in the app's header: a device list and a play button. Pick a device, press play, and MobAI builds the app (an incremental build) and runs it. While that device runs your app, the button turns into stop. MobAI remembers the device you picked for each app.
Debug or Release
The top of the device list switches between Debug and Release. Debug opens with hot reload. Release builds what you ship: a Release build for the simulator, a Release APK signed with the app's upload key on Android, and a Release build signed for your iPhones. Flutter runs only Debug on a simulator, so there Release runs Debug. MobAI remembers the choice for each app, and your agent builds the same way unless it asks for the other.
The list groups iPhones, Android phones, simulators and emulators. A green dot means hot reload is running on that device. When nothing is connected, MobAI offers to start a simulator or an emulator. It asks before making a new emulator, since that can download a system image.
Hot reload
A Debug build of a Flutter or React Native app opens with hot reload. Flutter attaches to the installed build, React Native starts Metro. Save a file, or let the agent change one, and the app on the device updates. The session belongs to the device, so an agent using that device reloads it too. It stops when the device goes away.
When a simulator app crashes, the error line says why, from the crash report it left.
Where builds run
| Build | Mac with Xcode | Windows, Linux, or a Mac without Xcode |
|---|---|---|
| iPhone simulator | This computer | A CI simulator on GitHub |
| iPhone, Debug | This computer | GitHub Actions |
| TestFlight | This computer | GitHub Actions |
| Android, Debug and release | This computer | This computer |
Local iPhone builds are signed by the account in Xcode's settings. Before a signed build, MobAI checks that the project's team is one Xcode can still sign for, and if not, asks which team to use.
GitHub builds run in your own repository on your own Actions minutes. MobAI commits the build workflow to the default branch, after your yes, and saves the secrets it needs. Your uncommitted changes go with the build.
The Builds tab lists every build with where it ran, its size and its log. A failed build keeps the end of its log. Run any build on a device from its detail. MobAI builds one thing at a time per app.
Build commands
Every build runs a command from mobai.json, a file at your app's folder (or above it, in a monorepo)
that you commit. The same commands run on your computer, on GitHub, on your other Macs and in CI,
so one file decides how the app builds everywhere.
{
"build": {
"ios": {
"debug": {
"command": "flutter build ios --simulator --debug",
"app": "build/ios/iphonesimulator/Runner.app"
},
"device": {
"command": "flutter build ipa --debug --export-options-plist \"$MOBAI_EXPORT_OPTIONS\"",
"app": "build/ios/ipa/*.ipa"
},
"release": {
"command": "flutter build ipa --export-options-plist \"$MOBAI_EXPORT_OPTIONS\"",
"app": "build/ios/ipa/*.ipa"
}
},
"android": {
"debug": {
"command": "flutter build apk --debug",
"app": "build/app/outputs/flutter-apk/app-debug.apk"
},
"release": {
"command": "flutter build appbundle --release",
"app": "build/app/outputs/bundle/release/app-release.aab"
},
"releaseApk": {
"command": "flutter build apk --release",
"app": "build/app/outputs/flutter-apk/app-release.apk"
}
}
}
}
debugis the iPhone simulator build and the Android Debug build.deviceis the iOS Debug build for iPhones, the one Run on device installs and hot reloads.releaseis the store build: an IPA for TestFlight, an App Bundle for Google Play.simulatorRelease(iOS) andreleaseApk(Android) are the Release builds a simulator, an emulator or a phone installs. CI tests and Run's Release mode use them. Flutter has nosimulatorRelease, since it builds only Debug for the simulator.commandruns in the folder that holdsmobai.json, with bash. On Windows without bash MobAI uses a built-in shell, so./gradlewstill works.appis where the build lands, relative to that folder. It can be a pattern, and the newest match wins.- iOS release builds get
$MOBAI_EXPORT_OPTIONS, the export options MobAI writes for the build.
Signing never goes into this file. Edit the commands from Build commands in the app's menu, or ask an agent to. MobAI writes defaults only for entries that are missing, and never rewrites yours.
iPhone signing
To run your app on an iPhone without Xcode, pick one way to sign it in iPhone signing (the app's menu). MobAI sets it up once and signs every build with it, on GitHub or on this computer.
- Apple ID. Any Apple ID, free accounts included. MobAI signs the build on this computer for the iPhone you run it on.
- App Store Connect key. For a paid developer account. MobAI asks Apple for the certificate and the profile, and adds new iPhones and the capabilities your app uses as they come up.
- Your own files. A
.p12and a development profile you already have. MobAI checks them when you save them.
A build whose profile doesn't list the iPhone, or runs out within 12 hours, is signed again before it installs. App extensions get their own profiles. MobAI keeps one certificate per team and type and reuses it, so you don't hit Apple's certificate limit.
When Apple needs something only you can do on its site, like an App Group, MobAI stops and links the page.
CI simulator
Without a Mac, Run on CI simulator starts an iPhone simulator on a GitHub Actions macOS runner in your repository. It builds your app there, shows the simulator in MobAI and runs it with hot reload for Flutter and React Native. Your agent uses it like any other device.
With Release picked in Run on device, it runs your app's Release build instead, without hot reload.
Once an app has run on one, Run latest build skips the native build and reuses the last one. A CI simulator stays up 10 minutes after it joins, then closes 5 minutes after the last use. Stop it from the device list.
Have a Mac somewhere else? Use another Mac (in the agent session's device menu) gives you three commands to run on it. Its simulator then shows up in MobAI, and rebuilds send your folder over.
TestFlight
Upload to TestFlight builds a store build and uploads it. The first time, MobAI asks for your App Store Connect
key (the issuer ID, key ID and .p8 file) and keeps it in your system keychain. It sets up store signing
on the way. Each upload from the header asks once, showing where it goes and the last upload.
Create the app's record in App Store Connect first. If it's missing, or the key is revoked or lacks App Manager access, MobAI says which and what to do.
Google Play
Upload to Google Play builds a signed App Bundle and sends it to the track you pick. Each upload asks first, and production gets a clear warning.
- Upload key. MobAI makes one for the app, or you import yours. Its passwords stay in your keychain and reach Gradle only during the build.
- Service account. Add the Play service account key once. MobAI lists the steps with links to each console page.
- First upload. Google takes a new app's first bundle only through Play Console. MobAI builds it anyway, shows Google's message and where the file is, so you can upload it by hand.
Deleting an app deletes the upload key MobAI made for it. The delete confirm says so and offers Save a copy.
Set up CI
Set up CI runs every step it can in a row and shows each one as it goes:
- GitHub: sign in, or put the folder on GitHub.
- A Testing project for the app, saved in
mobai.json. - The
MOBAI_API_KEYrepository secret. Paste a MobAI token or generate one. Save for other apps keeps it in your keychain for the next repository. .github/workflows/mobai-tests.yml, committed to the default branch after your yes. A protected branch gets a pull request instead.
The workflow runs on pull requests, on pushes to the default branch and from Run tests now. It runs your
.mob tests on an iPhone simulator and an Android emulator, and skips the device jobs until the
repository has a test. Results show up on the app's page and in the Testing dashboard.
The tests run your app's Release build, the one you ship (a Flutter app runs Debug on the simulator). On Android each run signs the build with a key it makes and throws away, so your upload key never leaves your computer.
MobAI never replaces a workflow that's already there. When a newer MobAI has a better one and nobody has edited the file since MobAI committed it, Set up CI offers Update the workflow, and the commit asks first.
No tests yet? Write first test puts a prompt for your agent in the box.
Pull request checks
The Pull requests tab lists the repository's open pull requests. Test this PR checks the branch out in its own folder, outside your working copy, and starts your agent on it. The agent reviews the diff, builds the app, tries the change on a device and sums up.
- A pull request from a fork waits for your yes before anything builds, and its review runs without the fork's agent settings.
- Turn on GitHub triggers to start a check from GitHub: the
mobai-testlabel, or a/mobai testcomment from an owner, member or collaborator. - Checks write nothing to GitHub.
Commits
MobAI never commits or pushes to your repository without your yes. Before each commit it shows the message, every file with its text, and where it goes. Say no and the file stays changed in your folder.
Local commits push your current branch with your own git credentials. A protected branch gets a mobai/
branch and a pull request. MobAI never pulls or merges into your folder, except a fast-forward right after its own
GitHub commit, when you're on that branch.
Agents commit their own work. MobAI doesn't commit for them: when a call needs a file on GitHub, MobAI writes it into the folder and tells the agent to commit and push it.
Some things never ask, because they don't land in your history: build snapshots, shared build drafts and pull request checkouts.
Skills
Skills are short guides for agents: how to build, run, test, set up and release this kind of app with MobAI.
MobAI picks them by framework and adds them to .claude/skills and .agents/skills.
Opening an app updates skills you haven't changed. A skill you or an agent edited is never overwritten. MobAI skills in the app's menu shows them, with Replace with MobAI's and Keep yours for edited ones, and Revert to remove the ones MobAI added.
What stays where
- Apps, builds, agent chats and check screenshots live on this computer. MobAI's servers aren't asked about your apps at all.
- Keys and passwords (App Store Connect, Google Play, the upload key, signing files) go into your system keychain.
- MobAI commits only what you approve, like
mobai.jsonand workflows. Skills are written into the folder and left for you to commit. - Test results go to your Testing project when CI runs.
- Your agent sends what it needs to its own provider, as it does without MobAI.
Set up with agent
The app's page shows a What's next row: the parts not set up yet, like signing, TestFlight, Google Play or CI. Click one to start your agent on it, or Set up with agent for all of them.
The agent does the setup through MobAI's local API. It never asks for or sees a key. When a part needs something only you can enter (a GitHub sign-in, an App Store Connect key, a Play key, a MobAI token, a new repository's name), MobAI opens its own form in your window. The agent gets a message saying how you answered, without the value.
HTTP API
Agents MobAI starts get MOBAI_API_URL and MOBAI_APP_ID in their environment. The
routes live on MobAI's local HTTP API under /api/v1. Errors come back as 422 with one sentence that says what to do.
API="${MOBAI_API_URL:-http://127.0.0.1:8686}/api/v1"
curl -s "$API/apps/$MOBAI_APP_ID"
| Route | What it does |
|---|---|
GET /apps/{id} | What builds where, build kinds, TestFlight and Play status, skills, setup state |
POST /apps/{id}/builds | Start a build: platform, kind, optional dir (a git worktree of the app) and signing (files, key, appleid) |
GET /apps/{id}/builds | List builds. /builds/active shows the running one |
POST /apps/{id}/builds/cancel | Cancel the running build or its upload |
POST|GET /apps/{id}/run | What Run on device does: build for a device (device_id) in configuration debug or release (the app's own choice when left out), then run it. GET follows it |
POST /apps/{id}/builds/{buildId}/install | Install a build on a device (device_id). /run also opens it, with hot reload for Debug Flutter and React Native builds |
POST|DELETE /apps/{id}/builds/{buildId}/share | Share a build with the team, or stop. GET /builds/shared lists teammates' builds |
DELETE /apps/{id}/builds/{buildId} | Delete a build. POST /builds/delete takes {"builds": [ids]} |
GET|POST /apps/{id}/testflight | TestFlight status, or upload |
GET|POST /apps/{id}/play | Google Play status, or upload to {"track": ...} |
GET|POST /apps/{id}/ci/runs | CI runs, or start one. /ci/runs/{runId}/log and /rerun |
POST /apps/{id}/ci-simulator/stop | Stop the app's CI simulator |
POST /apps/verify | Run a check on a device and keep its screenshots (the verify_changes MCP tool) |
iOS build kinds are simulator, device, development, store and
ci-simulator. Android kinds are debug and release.
Setup routes
Each part has a GET for its state and a POST .../setup that does everything MobAI can for it.
A setup is safe to repeat.
| Part | Routes |
|---|---|
| Repository | /apps/{id}/repo |
| CI | /apps/{id}/ci |
| Skills | /apps/{id}/skills |
| Builds | /apps/{id}/build/github, /apps/{id}/build/local |
| Signing | /apps/{id}/signing/ios ({"type": "development" | "store"}), /apps/{id}/signing/android |
| Releases | /apps/{id}/release/testflight, /apps/{id}/release/play |
A setup answers with its state, a note for the agent, and a userAction when only the user
can do the next step. When it opens a form in MobAI it answers 202 with {"waiting": "..."} at once,
and the work goes on. Changes a call left in the folder come back as uncommitted, for the agent to commit.
The setting-up-mobile-apps skill teaches agents all of this.
Troubleshooting
"There is no iOS debug build command in mobai.json yet"
MobAI couldn't work out how your project builds. Add the entry in Build commands (the app's menu), or ask your agent to.
An iPhone build fails over signing
Open iPhone signing and check the picked way is set up. With your own files, the profile has to list the iPhone. The error links the Apple page when the fix is there.
The commit sheet says the folder is behind
Your folder is behind the branch on GitHub, so what MobAI reads from it may be out of date. Pull, or ask your agent to, then try again.
A GitHub build can't push
MobAI pushes build snapshots with your own git credentials first, then your GitHub sign-in. Connect GitHub in MobAI if neither works.
Questions or a bug? Write to [email protected]. Back to Apps.