ios-simulator-skill: an accessibility-first toolchain for driving iOS apps from Claude Code
An IOS Simulator Skill for ClaudeCode. Use it to optimise Claude's ability to build, run and interact with your apps, and to proxy xcodebuild to save token and context wastage.
At a glance
- What is it?
- A 27-script Python skill that wraps xcodebuild and simctl so an agent can build, navigate and inspect an iOS app without drowning in raw tool output. Its real bet is that accessibility labels, not pixel coordinates, are what an LLM should click on.
- Who is it for?
- Adopt it if you already run Claude Code on macOS and want the agent to drive a simulator through accessibility labels rather than guessed coordinates, and if the build output is currently eating your context window. Skip it if you are not on macOS 12+ with Xcode command line tools, or if you need unattended CI runs: the README lists no CI story and IDB, which several interactive features depend on, is optional rather than bundled.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The context window is the constraint, not the simulator
An agent building an iOS app has two expensive resources: tokens and the patience of the person watching the transcript. Raw xcodebuild output is the first drain. A single compile can emit hundreds of lines, most of them irrelevant to the failure at hand, and every one of those lines lands in the conversation and stays there. The second drain is the screenshot. The README puts a screen analysis at 200+ lines of raw tool output against 5 lines from this skill, and describes the accessibility tree as roughly 10 tokens of default output versus 1,600 to 6,300 tokens for a screenshot. Those are the project's own figures, not independent measurements, but the direction of the claim is easy to check: a screenshot is an image, and images cost more tokens than a short list of element labels.
The intended user is a developer running Claude Code on a Mac who wants the agent to actually operate the app, not just edit the Swift files and hand back a diff. The repository describes itself as a skill for building, testing and automating iOS apps, with 27 scripts written for both humans and agents. If you only want the build half, the README points at a separate plugin, xclaude-plugin, and if you would rather have an MCP server than a skill, it points at XC-MCP.
Progressive disclosure in build_and_test.py
The central mechanism is not clever parsing of Xcode output. It is refusing to show the output at all until asked. The README gives this example of what a build returns: a single line reading Build: SUCCESS (0 errors, 3 warnings) followed by an xcresult identifier such as xcresult-20251018-143052. That identifier is the handle. When the agent or the developer needs the detail, three flags pull it out on demand: --get-errors, --get-warnings and --get-log, each taking the xcresult ID.
This is a deliberate inversion of how xcodebuild is normally used. Instead of piping the whole log into a file and grepping it, the skill stores the result bundle and exposes accessors. The consequence is that an agent can iterate on a build loop without accumulating log noise, and can request the error list only when the summary line shows a nonzero error count. The trade-off is an extra round trip: you cannot see the error text in the same turn as the build result. For a human at a terminal that is mildly annoying. For an agent, it is the whole point.
Navigation by accessibility label instead of coordinates
The README contrasts two ways to tap a login button. The fragile one is idb ui tap 320 400. The one this skill uses is python scripts/navigator.py --find-text "Login" --tap. The difference is not stylistic. A coordinate is a fact about one render of one screen at one device size. An accessibility label is a fact about the app's intent, and it survives layout changes, font size changes and a different simulator.
navigator.py accepts --find-text, --find-type and --find-id, and can either tap the match or enter text into it. Around it sit scripts that operate on the same tree: screen_mapper.py lists the interactive elements on the current screen, with --verbose and --hints for more detail; accessibility_audit.py runs WCAG checks against the current screen. The README links to a longer piece by the author on why accessibility-first navigation matters for agents, which is worth reading as the design rationale rather than as evidence.
The honest caveat is that this only works when the app exposes accessibility information. An app built with custom drawing, a game rendered in Metal, or a view hierarchy where every element is unlabelled will give navigator.py nothing to find. In that case you are back to coordinates, and the skill's advantage largely disappears. The same applies to anything you want to verify visually: visual_diff.py compares two screenshots with a --threshold, which is the fallback path, not the primary one.
Installing it and checking the environment
There are two installation routes. The recommended one is inside Claude Code itself: /plugin marketplace add conorluddy/ios-simulator-skill followed by /plugin install ios-simulator-skill@conorluddy. The manual route is a git clone into either ~/.claude/skills/ios-simulator-skill for a personal install or .claude/skills/ios-simulator-skill inside a project. Restart Claude Code afterwards and the skill loads automatically.
Prerequisites are macOS 12 or later, Xcode command line tools via xcode-select --install, and Python 3. Two dependencies are explicitly optional. IDB, installed through brew tap facebook/fb && brew install idb-companion, is needed for the interactive features. Pillow, installed with pip3 install pillow, is needed for visual diffs. Read that word optional carefully: several of the scripts that make the skill interesting are interactive, so a bare install without IDB is a smaller tool than the feature list suggests.
The repository ships a check for exactly this. sim_health_check.sh verifies that Xcode, simctl, IDB and Python are all present. The README lists no flags for it, so it appears to be a pass or fail report rather than a configurable diagnostic. Run it before anything else, because the failure mode of a missing IDB is a script that errors at the point of use rather than at install time.
What the other scripts cover, and where the seams are
The 27 scripts divide into build, device state, navigation, testing, permissions and lifecycle. The README states that every script supports --help and --json, which matters more than it sounds: --json is what lets an agent parse a result without scraping prose, and --help means the agent can discover flags at runtime rather than relying on the prompt.
Some of the coverage is broader than the headline feature. container.py inspects the app sandbox, listing files, catting them, reading UserDefaults and Core Data, and exporting. model_inspector.py reads Core Data and SwiftData models straight from project files, with --raw and --show-versions. localization_audit.py checks .xcstrings catalogs for missing keys, unused keys and placeholder mismatches, with a --strict mode. privacy_manager.py grants, revokes and resets permissions across 13 services. push_notification.py sends simulated pushes. status_bar.py overrides the clock, battery and network indicator, which is the standard trick for making screenshots deterministic.
Two entries deserve a closer look because they carry the most caveats. hang_watcher.py, also called HangBuster, records and summarises os_log hang events. Its flag set is the most elaborate in the repository: --start with --raw-capture, --max-size-mb and --no-gzip, plus --stop, --get-details, --list-sessions, --diff, --budget-tokens and --auto-sample, with --watch and --since marked as legacy. The README notes it auto-restarts when the stream dies and cleans up disk when a cap is hit. That is a lot of machinery for one script, and the presence of a --budget-tokens flag suggests the author has hit token limits on log output in practice. The legacy flags also suggest the interface has moved at least once.
The second is the release history. v1.3.0 added simulator lifecycle management, v1.3.1 was documentation refinement, and v1.4.0 brought a plugin marketplace and the model inspector. The gap between v1.3.1 in October 2025 and v1.4.0 in April 2026 is roughly six months, which is worth knowing if you are pinning to a version. The README is truncated in the material available here, so the device lifecycle table is incomplete and I cannot describe the remaining scripts with confidence.
The IDB dependency is the sharpest edge
The clearest limitation is structural rather than a bug. The skill is a wrapper. It does not talk to the simulator through a private channel; it shells out to xcrun simctl and to idb, and it parses what comes back. That means every failure mode of the underlying tools is still yours. If idb-companion is not installed, the interactive scripts do not work. If simctl changes its output format in a future Xcode, the parsing may break, and the skill's release cadence is measured in months, not weeks.
The second limitation is the accessibility dependency described earlier. An app with poor labels is a poor fit, and the irony is that accessibility_audit.py will tell you so. If your app fails that audit, navigator.py will struggle for the same reason.
The third is the missing CI story. The README describes a skill for Claude Code, installed into a Claude Code skills directory or via a plugin marketplace. It does not describe running these scripts headlessly in a pipeline. Nothing stops you from invoking the Python files directly from a shell, and the --json flag is clearly built for machine consumption, but that path is not documented here. Treat unattended use as something you would have to establish yourself rather than something the project hands you.
Against XC-MCP and the plain xcodebuild workflow
The author names the alternative directly. XC-MCP, at conorluddy/xc-mcp, is the same author's MCP server for the same problem space. The difference in approach is architectural. An MCP server runs as a separate process and exposes tools over the Model Context Protocol, so any MCP-capable client can call it. A skill is a directory of scripts plus instructions that Claude Code loads into its own context, so the agent reads SKILL.md and then runs shell commands. The skill approach keeps everything inside one conversation and one tool loop. The MCP approach decouples the tooling from the client, at the cost of running and configuring another server.
There is also a narrower split inside the same repository family. The README points at xclaude-plugin for people who want the Xcode build tooling without the simulator scripts. That is the right choice if your app is a library, a package, or something you test on device rather than in a simulator.
The comparison that matters most is against doing nothing. Running xcodebuild by hand and piping to a log file works, and a developer who knows their scheme already has the muscle memory. What they do not have is a way to hand an agent a semantic handle on the UI. That is the gap this fills, and it is a real one if you are trying to get an agent to reproduce a bug or walk a flow.
Maintenance cost and the MIT licence
The maintenance surface is Python 3 plus a shell script, with two optional native dependencies. There is no compiled artifact to keep in step with Xcode, which is the main reason a wrapper like this stays usable across Xcode upgrades: when simctl output shifts, the fix is a parser change in a .py file, not a rebuild. The cost you carry is the cost of the underlying tools. If Facebook's idb-companion stops being maintained or stops building on a future macOS, the interactive half of the skill goes with it. That is a dependency you do not control and cannot patch from this repository.
The licence is MIT, which permits commercial use, modification and redistribution provided the copyright notice and permission notice are retained. That is the plain reading of MIT and not legal advice; if you are vendoring the scripts into a proprietary product, have someone check how you attribute it. The README does not discuss licence obligations, and the repository does not appear to ship a separate commercial tier or hosted service that would complicate the picture.
Editorial conclusion
Adopt it if you already run Claude Code on macOS and want the agent to drive a simulator through accessibility labels rather than guessed coordinates, and if the build output is currently eating your context window. Skip it if you are not on macOS 12+ with Xcode command line tools, or if you need unattended CI runs: the README lists no CI story and IDB, which several interactive features depend on, is optional rather than bundled. Before trusting it, run scripts/sim_health_check.sh to confirm Xcode, simctl, IDB and Python are all visible, then run build_and_test.py against one real scheme and check that the xcresult ID it prints is one you can drill into with --get-errors.
Community notes