Build and test your Capacitor iOS app on a cloud Mac
Compile, run the iOS Simulator and archive a Capacitor app on a disposable cloud Mac from Windows or Linux, driven by you or your AI agent. Then ship every later web change over the air.
Capacitor lets you write one web app and ship it to iOS and Android. The catch is iOS: the native shell has to be compiled with Xcode, and Xcode only runs on macOS. On Windows or Linux you can build the web app and the whole Android app, but not the iPhone binary.
This guide uses a disposable cloud Mac from our friends at NoMac to do the Apple part. You (or your AI agent) start a Mac, build and test the iOS app, and delete the Mac. After that, most changes never need a Mac again, because OtaKit ships web changes over the air.
The split that saves you Mac time: native changes (plugins, permissions, Capacitor upgrades) need a new iOS build. Web changes (HTML, CSS, JavaScript) can go out as an OTA update. Most day-to-day work is the second kind.
1. Start a cloud Mac
NoMac sessions are fresh macOS VMs with Xcode, Node, CocoaPods and an iOS simulator runtime preinstalled, billed by the second. From your project directory on Windows (WSL), Linux or a Mac:
npm install -g @nomac/cli nomac login nomac start --json nomac sync .
sync skips node_modules, dist, Pods and anything in .gitignore, so the upload is just your source.
2. Build the web app and sync iOS on the Mac
If you want over-the-air updates later, add the OtaKit plugin now, locally, before this build: npm install @otakit/capacitor-updater. It is native code, so it has to be inside the binary you ship. Then:
nomac sync . nomac ssh -- 'npm ci && npm run build && npx cap sync ios'
cap sync copies your web build into the iOS project and installs native dependencies, whether your project uses Swift Package Manager or CocoaPods.
3. Compile and run it in the simulator
# Swift Package Manager projects (the Capacitor 8 default) nomac ssh -- xcodebuild -project ios/App/App.xcodeproj -scheme App \ -destination 'platform=iOS Simulator,name=iPhone 17 Pro' -derivedDataPath build build # CocoaPods projects use the workspace instead # -workspace ios/App/App.xcworkspace nomac ssh -- xcrun simctl boot "iPhone 17 Pro" nomac ssh -- xcrun simctl install booted build/Build/Products/Debug-iphonesimulator/App.app nomac ssh -- xcrun simctl launch booted com.example.app nomac ssh -- 'mkdir -p shots && xcrun simctl io booted screenshot shots/home.png' nomac pull ./shots --from shots
A first simulator boot takes about 90 seconds. The screenshot shows whether your web view actually loaded, which catches the classic Capacitor mistakes: a wrong webDir, a missing build, or a blank screen from a JavaScript error.
4. Let your AI agent do it
NoMac also works as an MCP server, so Claude Code, Codex or Cursor can run the steps above themselves:
claude mcp add nomac -- npx @nomac/cli mcp claude mcp add otakit -- npx -y @otakit/cli@latest mcp
With both connected, one agent can build the native shell on a Mac when it must, and ship web changes through OtaKit when it can. See shipping Capacitor updates with AI agents.
5. Archive for TestFlight, then stop
Signed builds need your Apple Developer account and signing set up on the session. Once the release is uploaded, stop the Mac; it is deleted along with everything on it:
nomac stop --json
If you prefer CI for signed releases, see iOS signing in GitHub Actions. The cloud Mac is then your workbench for debugging, and CI is the factory.
6. Ship everything else over the air
Once a build with the OtaKit plugin is in users' hands, web-only fixes go straight to devices from your own machine, with no Mac, no Xcode and no App Review wait:
npm run build npx -y @otakit/cli@latest upload --release
You only return to the Mac when something native changes. For a lot of apps, that is a few times a year.