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-owned | Agent-owned |
|---|---|
.pbxproj / .xcodeproj | Swift sources under the target |
| Code signing & capabilities | SwiftUI views, models, tests |
| App Store Connect / privacy labels | Build-error fixes via MCP |
| Entitlements (HealthKit, Push, …) when first enabling | Refactors that stay inside existing patterns |
| Visual polish on device | Scaffolding 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.
| Runtime | Best for | Closed Sim Loop | Project File Firewall | Notes |
|---|---|---|---|---|
| Claude Code CLI | Deep multi-file features, autonomous fix loops | Yes (via MCP) | Hooks (mature) | Primary implementer for most practitioners |
| Cursor | Diff review, UI tweaks, Figma/screenshot paste, accept/reject | Yes (MCP config) | Via rules + review | Daily IDE shell; pin Claude Code in terminal |
| Gemini CLI | Cheap bulk analysis, audits, second opinions | If MCP wired | Weaker rule following | Strong for exploration; nudge project rules often |
| Xcode 26.3+ / 27 Intelligence | Inline one-file fixes inside Xcode | Partial (native tools) | N/A (uses Xcode) | No CLAUDE.md/hooks; weak for full-project autonomy |
| Codex CLI | Headless batch, dual-model review | Yes (MCP) | Manual / weaker hooks | Claude 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:
| Category | Examples | Why it matters |
|---|---|---|
| Build | build_sim, build_device | Structured JSON errors by file/line — not 4K lines of log |
| Test | test_sim | Per-method fail signals |
| Sim lifecycle | list_sims, boot_sim | No memorized simctl flags |
| Discovery | discover_projs, list_schemes | Agent does not guess your scheme name |
| UI | screenshot, tap, type_text | Closes 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(notObservableObject/ 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)
- Write
CLAUDE.md(orAGENTS.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. - Install XcodeBuildMCP; optional Argent / mcpbridge.
- Install the Project File Firewall hook.
- 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.
| Step | Owner |
|---|---|
| Pick one task | Human |
| Implement + build/test/screenshot loop | Claude Code + XcodeBuildMCP |
| Review diffs, UI tweaks | Cursor |
| Optional architecture audit | Gemini CLI |
| Add new files to Xcode target if needed | Human |
| Device run for sensors / signing paths | Human |
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
| Strong | Weak |
|---|---|
| SwiftUI views & navigation | .pbxproj / multi-target structure |
SwiftData CRUD & @Query patterns | Code signing & capability plumbing |
| Build-error diagnosis via MCP | Visual design taste |
| Boilerplate tests | Bluetooth / exotic hardware stacks |
| PRD → vertical feature slices | Metal/GPU correctness on device |
| Refactors inside documented patterns | Brand-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:
- The Terminal Was the First Agent Harness — deterministic wrappers beat raw model outputs.
- The 14K Token Debt — harness and context engineering compound more than one-shot prompting.
- Your MCP Servers Are Costing You 10 Seconds — tool schemas are not free; wire only the servers that close your loop.
- Claude Code vs Gemini CLI — control gradient: gate deep iOS writes; use autonomous tools carefully behind firewalls.
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:
- Blake Crosley — Building iOS Apps with AI Agents: The Practitioner’s Guide
- Vivian Qu — Building iOS Apps with Agents
- XcodeBuildMCP / GitHub
- Software Mansion — Argent: agentic iOS simulator control
- Donny Wals — Delivery pipeline for agentic iOS projects
- Apple — Xcode agentic coding / MCP bridge (Xcode 26.3+, Xcode 27 Intelligence)
- Yao et al. — ReAct