agentleFS
MKS-01MKS-01/appscreenmap/.claude/skills/ios-simulator/SKILL.md

ios-simulator

Skill2 starsChanged 17 days agoMITView on GitHub

Set up, run and debug AppScreenMap against an iOS Simulator — building the app for the simulator, booting it (Xcode 27 Device Hub), installing idb, reading the accessibility tree, recording a test fixture, and diagnosing a bad iOS crawl (missing tab bar, in-app browser, flat trees, stuck screens). Use for any iOS work: `crawl --platform ios`, `doctor` showing no simulator, an iOS crawl that stops early or maps too little, or extending `drivers/ios.py` / `drivers/ios_hierarchy.py`.

  • Installs packages

8 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use android-emulator.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • eigenwalletSkill

    android-emulator

    Drive the Android emulator for realistic end-to-end tests of the app's Android features and UI (install APK, puppeteer the UI, read backend logs). Use whenever a change needs verification on Android.

    Also in 3 projects that use this

The file

Files
SKILL.md

iOS Simulator: set up, run, debug

AppScreenMap drives a simulator with two tools, because neither does the whole job:

ToolDoesCannot
xcrun simctlboot/shutdown, install, launch/terminate, screenshots, permissions (privacy), Dynamic Type / appearance / contrast (ui), status bar, keychain, push, openurltap, swipe, type, read the UI tree
idb (idb_companion + fb-idb client)accessibility tree (ui describe-all), tap / swipe / text / key, ui quiet (idle + no animations), scroll, rotate, hinge, crash list, video, xctracebuild or install simulators

Code: packages/drivers/src/appscreenmap/drivers/ios.py (the driver the explorer calls) and ios_hierarchy.py (iOS tree → the uiautomator-style XML the analyzer reads). Design and status: docs/PLAN.md §8.3.

1. Preflight

uv run appscreenmap-engine doctor     # needs: a booted simulator, idb client, idb_companion
brew install facebook/fb/idb-companion
uv tool install fb-idb         # puts `idb` on PATH (uvx --from fb-idb idb also works, ~1 s slower)
xcrun simctl list devices available | head
xcrun simctl boot "iPhone 17"
  • Xcode 27 has no Simulator.app. Its replacement is Device Hub: open /Applications/Xcode.app/Contents/Applications/DeviceHub.app. A simulator also runs headless, so a booted device with no window is normal; open Device Hub to watch it.
  • idb talks to a companion process. The driver finds the booted simulator on its own; to check by hand: idb_companion --udid <UDID> --grpc-port 10882 & then idb connect localhost 10882, then idb ui describe-all --udid <UDID>.
  • One booted simulator is picked automatically; with several, pass --serial <udid>.

2. Build the app

A simulator runs a .app from an iphonesimulator build. An .ipa is device-signed (arm64, provisioning) and will not run there; the driver rejects it. There's no iOS sample in the repo yet (a SwiftUI Playground with the same scenarios as the Android one is planned, docs/PLAN.md), so build the user's app:

cd path/to/YourApp
xcodebuild -project YourApp.xcodeproj -scheme YourApp -sdk iphonesimulator -configuration Debug \
  -destination 'platform=iOS Simulator,name=iPhone 17' -derivedDataPath <scratch>/dd CODE_SIGNING_ALLOWED=NO build
# → <scratch>/dd/Build/Products/Debug-iphonesimulator/YourApp.app   (use -workspace for CocoaPods / KMP projects)

A Kotlin Multiplatform app's first build also compiles the shared framework (minutes); later ones are fast. Don't edit the app's source unless asked: its owner fixes what the crawl finds and re-crawls to verify.

3. Crawl

uv run appscreenmap-engine crawl --platform ios --app <path>/YourApp.app --clear-data --max-steps 150
uv run appscreenmap-engine serve        # the run is in runs/<app>-ios/

--font-scale 2.0 sets Dynamic Type to accessibility-large (WCAG 1.4.4). Load times and system permission alerts aren't supported yet. --package <bundle id> re-crawls an already installed app.

4. How the iOS tree behaves (what surprised us)

  • Flat. SwiftUI exposes one flat list under the Application node; --nested doesn't add structure. Grouping-based detection (repeated list items) has less to go on than on Android.
  • Coordinates are points (402×874 on an iPhone 17); screenshots are 3× pixels. The driver reports density: 160 so px-per-dp = 1 and the a11y checks read points as dp.
  • Off-screen nodes are included (the tree lists the whole scroll content); ios_hierarchy clips to the screen and drops what isn't visible.
  • Scrollable appears in every node's traits. It's noise; scrollability comes from the element type (ScrollView, Table…).
  • A standard tab bar comes back as one "Tab Bar" group without its tabs. The driver probes along it with describe-point and adds them as Tab nodes (about one extra call per tab).
  • Only what VoiceOver sees exists. A control with no label/identifier or that isn't an accessibility element is not in the tree, so the crawler can't see or tap it. That is a finding about the app, not a crawler bug.
  • Out-of-process UI has a one-node tree: an in-app browser (SFSafariViewController), share sheet, system alert. The driver reports package ios.external and the explorer restarts the app. Another app in front (Safari…) is matched by pid.
  • No activities and no Back key. Screens are told apart by structure + text. back() is the edge swipe.

5. Diagnose a bad crawl

SymptomLikely causeWhat to do
"nothing left to explore" after a few screensThe navigation (tab bar, close button) isn't in the accessibility treeidb ui describe-all on that screen; if the control is absent, it's an app finding. Check for .accessibilityLabel / traits in the Swift view.
Lots of x:com.apple.mobilesafari or ios.external screensLinks open Safari / an in-app browserExpected; they are recorded as external and the app restarts. Block the link label in blocked_text to stop tapping it.
Same screen mapped many timesLive content (timers, clocks, "1h ago") in the signatureCheck packages/core/tests/test_analyzer.py for the normalisation rules; digits are masked, words are not. Add a test, then tune like tune-crawler.
Blank elements listTree returned only the Application nodeA system sheet is in front, or the app hasn't drawn yet: take simctl io screenshot, raise --settle.
idb describe-all returned no treeCompanion not running / simulator not booted / wrong UDIDdoctor; idb list-targets; kill stray idb_companion and retry.
Tap does nothingOff-screen or covered element, or coordinates in pixelsTaps are points. idb ui describe-point x y shows what is there.
Crawl taps something destructiveLabel isn't in blocked_textAdd the wording to DEFAULT_BLOCKED_TEXT (config.py) with a test.

6. Record a test fixture

Tests never need a device. To capture a tree:

idb ui describe-all --udid <UDID> | python3 -c "import sys,json; d=json.load(sys.stdin); json.dump(d[:30], open('packages/drivers/tests/fixtures/<name>.json','w'), indent=1)"

Shorten long text (no personal content), keep the Application node first, and test the translation in packages/drivers/tests/test_ios_hierarchy.py (idb_to_xml → analyze). The driver takes an injectable runner (run=), so command output can be faked without a simulator.

7. Not used yet (worth a look)

idb ui quiet (a real "settled" signal instead of fixed waits), idb crash list (cleaner than scanning DiagnosticReports), idb xctrace record (Instruments traces for load times), idb rotation / idb hinge (orientation and fold conditions for the adaptive audit), idb video (screen recordings), simctl ui … increase_contrast|appearance (condition matrix), simctl status_bar (clean store screenshots), simctl push (notification flows).

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.