· 11 min read ·

The Closed Sim Loop: How Agents Actually Ship iOS Apps

Agents write Swift fine. They still fail to ship iOS apps until you close the build–sim–fix loop. Here is the end-to-end method with Claude Code, Cursor, Gemini CLI, and XcodeBuildMCP.

The Closed Sim Loop: How Agents Actually Ship iOS Apps

I mapped every serious writeup I could find on agentic iOS in 2026 — Crosley’s multi-app production guide, Qu’s Meta/meetup talk on verification loops, XcodeBuildMCP’s tool surface, Argent’s simulator control, Apple’s Xcode Intelligence agents, delivery pipelines into TestFlight — and the same failure repeated under different brand names.

Agents write SwiftUI. That part works.

They still fail to ship iOS apps. The failure mode is not “the model is bad at Swift.” It is that most agent sessions are compile-blind: they edit files, then wait for a human to paste xcodebuild output back into chat. On web, the verification loop is almost free — load a page, read the DOM, fix. On iOS, the loop is proprietary, slow, and until MCP servers wrapped the simulator, unreachable from the agent’s tools.

Blake Crosley, who has shipped multiple App Store apps with agents (SwiftUI, SwiftData, HealthKit, Live Activities, multi-platform targets), puts the boundary cleanly: agents excel at views, models, refactoring, and build-error diagnosis — and fail at .pbxproj edits, code signing, and visual judgment. The gap between “agent writes Swift” and “agent ships an iOS app” is bridged by configuration, not by better prompts.

This post is that configuration as a method — the same harness thesis as The Terminal Was the First Agent Harness and Claude Code vs Gemini CLI, applied to a stack that still thinks Xcode is the center of the universe.


Why Mobile Breaks the Default Agent Story

Web agent workflows improved first for a structural reason: the environment is inspectable. Headless browsers, DOM snapshots, HTTP status codes, and hot reload give the model continuous feedback. iOS does not.

Vivian Qu (Meta iOS) frames the problem as a slow verification loop: write Swift, wait for a multi-minute build, stare at a simulator or device, then try again. There is no true hot reload for vanilla SwiftUI, limited headless UI, and platform APIs that are closed-source and yearly-breaking. Models train on less of that surface than they do on React or TypeScript, so recovery depends more on harness than on weights.

Mobile evals still lag software-engineering benches. Google’s AndroidBench (2026) put frontier models well below the 80–90% bands common on SWE-style tasks; category variance is extreme — architecture can look fine while device/media and build/migration tasks collapse. iOS/Swift still lacks a robust public agent benchmark comparable to those suites. That is not a reason to wait. It is a reason to design for failure recovery.

In terminal harness terms: if 78% of chess losses were illegal moves, the fix was not a bigger model — it was a harness that rejected illegal outputs. iOS needs the same idea. The “illegal move” here is code that does not compile, does not launch, or does not match the screen you asked for.


Two Frameworks

1. The Closed Sim Loop

The Closed Sim Loop is the requirement that the agent own the full cycle without human paste:

write Swift
  → build_sim (structured errors by file/line)
  → fix
  → test_sim (per-method pass/fail)
  → launch / screenshot / tap / type
  → compare to acceptance criteria
  → stop or repeat

Without that loop, you have autocomplete with commit access. With it, a feature that would cost 5–10 human build–error–fix cycles becomes one autonomous session. XcodeBuildMCP exists to make the Closed Sim Loop a first-class tool surface — structured errors, not a bash soup of xcodebuild flags.

2. The Project File Firewall

The Project File Firewall is the hard rule that agents must not own Xcode project structure:

Human-ownedAgent-owned
.pbxproj / .xcodeprojSwift sources under the target
Code signing & capabilitiesSwiftUI views, models, tests
App Store Connect / privacy labelsBuild-error fixes via MCP
Entitlements (HealthKit, Push, …) when first enablingRefactors that stay inside existing patterns
Visual polish on deviceScaffolding from a written PRD

Enforce the firewall with a PreToolUse hook that blocks writes to *.pbxproj and .xcodeproj/**. Crosley calls this the single most important iOS agent rule. Create the Swift file; you add it to the target once. Agents that “helpfully” rewrite project files will cost you hours of recovery.

These two frameworks work together: the Closed Sim Loop makes the agent powerful; the Project File Firewall keeps that power from destroying the project.


The Runtime Stack (Who Holds Which Leash)

In Claude Code vs Gemini CLI, I described The Control Gradient — how much of the loop stays deterministic. iOS adds a second axis: who can talk to the simulator.

RuntimeBest forClosed Sim LoopProject File FirewallNotes
Claude Code CLIDeep multi-file features, autonomous fix loopsYes (via MCP)Hooks (mature)Primary implementer for most practitioners
CursorDiff review, UI tweaks, Figma/screenshot paste, accept/rejectYes (MCP config)Via rules + reviewDaily IDE shell; pin Claude Code in terminal
Gemini CLICheap bulk analysis, audits, second opinionsIf MCP wiredWeaker rule followingStrong for exploration; nudge project rules often
Xcode 26.3+ / 27 IntelligenceInline one-file fixes inside XcodePartial (native tools)N/A (uses Xcode)No CLAUDE.md/hooks; weak for full-project autonomy
Codex CLIHeadless batch, dual-model reviewYes (MCP)Manual / weaker hooksClaude builds, Codex reviews is a common pair

Recommendation for a solo shipper: Claude Code as the implementer, Cursor as the review surface, Gemini for occasional audits, Xcode for signing and final polish. Do not pick one tool and expect it to replace the rest. The agentic iOS workflow is multi-runtime by default.


MCP Is the Missing Harness Layer

MCP (Model Context Protocol) is how agents stop pretending bash is an API. For iOS, two servers matter.

XcodeBuildMCP (primary)

XcodeBuildMCP wraps xcodebuild, simctl, and related tooling into structured MCP tools. It works without Xcode open. Daily tools that matter:

CategoryExamplesWhy it matters
Buildbuild_sim, build_deviceStructured JSON errors by file/line — not 4K lines of log
Testtest_simPer-method fail signals
Sim lifecyclelist_sims, boot_simNo memorized simctl flags
Discoverydiscover_projs, list_schemesAgent does not guess your scheme name
UIscreenshot, tap, type_textCloses the loop on “does the screen exist?”

Install (auto-detects Claude Code / Cursor / Codex):

npx -y xcodebuildmcp@latest init

Or wire Claude Code manually:

claude mcp add XcodeBuildMCP \
  -s user \
  -e XCODEBUILDMCP_SENTRY_DISABLED=true \
  -- npx -y xcodebuildmcp@latest mcp

Cursor project config (.cursor/mcp.json):

{
  "mcpServers": {
    "XcodeBuildMCP": {
      "command": "npx",
      "args": ["-y", "xcodebuildmcp@latest", "mcp"]
    }
  }
}

Teach the agent to prefer MCP over bash. Without that line in CLAUDE.md, you will watch it invent 200-character xcodebuild invocations and burn context parsing unstructured text — the same class of waste I measured as Schema Gravity.

Apple xcrun mcpbridge (secondary)

Xcode 26.3+ ships an MCP bridge into a running Xcode process (docs search, live diagnostics, SwiftUI preview hooks). Useful when Xcode is already open. Incomplete as a primary loop: XPC dependency, permission friction, and no hooks. Keep XcodeBuildMCP as the default build path.

Argent (when UI/debug depth matters)

Argent adds agent skills + MCP for simulator drive, diagnostics, and profiling (including React Native stacks). Install: npx @swmansion/argent init. Use it when you need “reproduce, diagnose, fix” in one session beyond basic screenshot/tap.


The End-to-End Method (Six Phases)

This is the pipeline. Skip a phase and the later phases get noisier.

Phase 0 — Scope (human)

Write one sentence of problem + user. Cap the MVP at 3–7 screens. Defaults that raise agent success rate:

  • SwiftUI + @Observable (not ObservableObject / UIKit-first)
  • Last 1–2 major iOS versions (not only the day-one beta SDK)
  • iPhone, portrait-only for v1
  • Local models (SwiftData or simple state) before networking
  • Explicit non-goals: Bluetooth, complex Metal, multi-window iPad

Narrow scope is not laziness. It is reducing the surface where the model has no training signal.

Phase 1 — Scaffold (human + light agent)

Create the Xcode app project yourself. Open the folder in Cursor. git init. Do not let the first agent invent a .xcodeproj from a blank directory unless you accept thrash.

Optional: Vivian Qu’s SwiftUI Agent Toolkit as a skeleton with skills and docs hooks.

Phase 2 — Harness (30–60 minutes, once per project)

  1. Write CLAUDE.md (or AGENTS.md + Cursor rules) with six sections: identity (bundle ID, min iOS, Swift version), annotated file map, build commands, key patterns, hard never-do list, framework-specific notes.
  2. Install XcodeBuildMCP; optional Argent / mcpbridge.
  3. Install the Project File Firewall hook.
  4. Optional format-on-save (SwiftFormat) as PostToolUse.

A 40-line CLAUDE.md on a 14-file app outperforms a 60-file app with no onboarding. Crosley’s portfolio makes the same point with production data: legibility beats file count. Agent effectiveness tracks project documentation density, not LOC.

Minimal pattern block:

## Rules
- NEVER modify .pbxproj or .xcodeproj contents
- NEVER change deployment target or entitlements without asking
- NEVER use NavigationView — always NavigationStack
- NEVER use ObservableObject — always @Observable
- Prefer MCP: build_sim / test_sim over raw xcodebuild in Bash

Phase 3 — Spec before code

Do not “build the whole app.” Write a one-page PRD. Agent expands PRD → feature specs → task list. You approve. Implement one task per session.

Prompt shape that works:

Read CLAUDE.md and the PRD.
Implement ONLY Task 3: Settings screen with notification toggle.
Match existing Settings patterns.
After changes: build_sim, fix all errors, screenshot light+dark.
Do not modify .pbxproj. Stop when build is green and screenshots match acceptance.

Planning is not ceremony. Language-to-language conversion experiments (SwiftUI → other frameworks) fail harder when the agent sees the old code; they succeed more often when handed a clean spec. The same discipline applies to greenfield iOS.

Phase 4 — Slice-and-Sim implementation

Slice-and-Sim: one vertical slice (one screen or one data path), then Closed Sim Loop to green, then the next slice.

StepOwner
Pick one taskHuman
Implement + build/test/screenshot loopClaude Code + XcodeBuildMCP
Review diffs, UI tweaksCursor
Optional architecture auditGemini CLI
Add new files to Xcode target if neededHuman
Device run for sensors / signing pathsHuman

UI first with mock data beats networking-first. Placeholder asset skills and light/dark Dynamic Island checks (as thin wrappers around MCP screenshots) raise first-pass UI quality without burning context on public-domain image crawls.

Phase 5 — Quality gates

Every 3–4 features: expert review pass — kill duplicated patterns, update CLAUDE.md, dual-model review (Claude implements, Codex/Gemini critiques). PR bots on pull requests. CI (Bitrise / GitHub Actions) builds and tests on PR; archive + TestFlight on merge. Donny Wals’s delivery-pipeline writeup is the right mental model: agents go fast only if guardrails catch what humans miss under speed.

Phase 6 — Ship (mostly human)

Signing, privacy nutrition labels, store screenshots, review notes, real-device performance: agent-assisted at best, human-owned in practice. Treat submission as outside the Closed Sim Loop.


What Agents Do Well vs Poorly

StrongWeak
SwiftUI views & navigation.pbxproj / multi-target structure
SwiftData CRUD & @Query patternsCode signing & capability plumbing
Build-error diagnosis via MCPVisual design taste
Boilerplate testsBluetooth / exotic hardware stacks
PRD → vertical feature slicesMetal/GPU correctness on device
Refactors inside documented patternsBrand-new iOS 27 APIs pre-docs in weights

When the model was trained before WWDC, name new frameworks in CLAUDE.md and link Apple docs. Do not expect the agent to invent iOS 27 surface area from vibes.


Where This Breaks

1. Multi-target / complex project graphs. Shared frameworks, app extensions, and watch companions force project-file edits. Agents will invent wrong target membership. Keep the firewall; do structural work yourself.

2. Visual polish. Screenshots prove existence, not product taste. Dynamic type, haptics, motion, and “does this feel iOS” still need a human eye.

3. Device-only truth. Simulator MCP is not HealthKit authorization UX, not true Bluetooth, not Metal performance. Agents that “pass” on sim can still fail on device.

4. Schema Gravity and MCP tax. Loading XcodeBuildMCP (dozens of tools) plus Argent plus five web MCP servers recreates the token tax. Scope MCP per project. Prefer project-scoped servers for iOS work; do not boot the entire global zoo every session.

5. Over-autonomy without firewall. Full-auto Claude or Gemini on an unprotected repo will rewrite project files “to help.” Autonomy without the Project File Firewall is not speed — it is delayed failure.

6. React Native translation myths. Agents are poor at language-to-language ports. If you need RN later, rewrite from a cleaned product spec, not from a mechanical SwiftUI dump.


Do This Right Now

If you have a Mac and one afternoon:

# 1. Create a SwiftUI iOS app in Xcode (human). Then:
cd /path/to/YourApp
git init

# 2. Install the closed loop
npx -y xcodebuildmcp@latest init

# 3. Write CLAUDE.md with Rules + Prefer MCP (see above)

# 4. Smoke-test the loop
claude  # or Cursor Agent with the same MCP

First agent prompt:

Discover this Xcode project via MCP. List schemes and available simulators.
Run build_sim with no code changes. Report whether the baseline is green.

If that command fails, fix the harness before writing features. If it succeeds, you have the Closed Sim Loop. Everything else is product work.


What This Extends

This is the same thesis as the rest of this series, on a harder surface:

Agents will keep getting better at Swift. That will not remove the need for a Closed Sim Loop or a Project File Firewall. The models generate. The harness ships.


Sharad Jain builds agentic systems and writes about harness engineering at sharadja.in. Previously founding engineer at Autoscreen.ai; data science at Meta and Autodesk. This post continues a series on agent harnesses — start with The Terminal Was the First Agent Harness and Claude Code vs Gemini CLI.

Sources & further reading:

Read next