Ios simulator skill

29 production-ready scripts for iOS app testing, building, and automation.

by conorluddy·MIT license·★ 1,267 Stars on the repo·GitHub ↗

Use now

Files of Ios simulator skill

conorluddy/main1 file shown
SKILL.md
Show the full text414 lines

iOS Simulator Skill

Build, test, and automate iOS applications using accessibility-driven navigation and structured data instead of pixel coordinates.

Quick Start

# 1. Check environment
bash scripts/sim_health_check.sh

# 2. Launch app
python scripts/app_launcher.py --launch com.example.app

# 3. Map screen to see elements
python scripts/screen_mapper.py

# 4. Tap button
python scripts/navigator.py --find-text "Login" --tap

# 5. Enter text
python scripts/navigator.py --find-type TextField --enter-text "[email protected]"

All scripts support --help for detailed options and --json for machine-readable output.

Navigation Strategy

Always prefer the accessibility tree over screenshots for navigation. The accessibility tree gives you element types, labels, frames, and tap targets — structured data that's cheaper and more reliable than image analysis.

Use this priority:

  1. screen_mapper.py → structured element list (5-7 lines, ~10 tokens)
  2. navigator.py --find-text/--find-type/--find-id → semantic interaction
  3. Screenshots → only for visual verification, bug reports, or visual diff

Screenshots cost 1,600–6,300 tokens depending on size. The accessibility tree costs 10–50 tokens in default mode.

29 Production Scripts

Build & Development (2 scripts)
  1. build_and_test.py - Build Xcode projects, run tests, parse results with progressive disclosure

    • Build with live result streaming
    • Parse errors and warnings from xcresult bundles
    • Retrieve detailed build logs on demand
    • Options: --project, --scheme, --clean, --test, --verbose, --json
  2. log_monitor.py - Real-time log monitoring with intelligent filtering

    • Stream logs or capture by duration
    • Filter by severity (error/warning/info/debug)
    • Deduplicate repeated messages
    • Options: --app, --severity, --follow, --duration, --output, --json
Device State (2 scripts)
  1. appearance.py - Control simulator appearance: dark mode, Dynamic Type size, and locale/region

    • Toggle light/dark theme via xcrun simctl ui
    • Set Dynamic Type size with friendly aliases (XS through AX5)
    • Write locale and region defaults; optional app restart via --bundle-id
    • RTL flagged automatically for ar/he/fa/ur/yi locales
    • Options: --theme, --text-size, --locale, --region, --reset, --bundle-id, --udid, --json, --verbose
  2. location.py - Simulate GPS coordinates, named city presets, and GPX scenario playback

    • Fix a coordinate with --lat/--lng or pick a city with --city
    • Play a built-in scenario (City Run, Freeway Drive, etc.) via --gpx <scenario>
    • Animate multi-waypoint paths with configurable speed via --waypoints and --speed
    • Clear simulated location with --clear; list available scenarios with --list-scenarios
    • Options: --lat, --lng, --city, --gpx, --waypoints, --speed, --clear, --list-scenarios, --udid, --json, --verbose
Navigation & Interaction (5 scripts)
  1. screen_mapper.py - Analyze current screen and list interactive elements

    • Element type breakdown
    • Interactive button list
    • Text field status
    • Options: --verbose, --hints, --json
  2. navigator.py - Find and interact with elements semantically

    • Find by text (fuzzy matching)
    • Find by element type
    • Find by accessibility ID
    • Enter text or tap elements
    • Options: --find-text, --find-type, --find-id, --tap, --enter-text, --json
  3. gesture.py - Perform swipes, scrolls, pinches, and complex gestures

    • Directional swipes (up/down/left/right)
    • Multi-swipe scrolling
    • Pinch zoom
    • Long press
    • Pull to refresh
    • Options: --swipe, --scroll, --pinch, --long-press, --refresh, --json
  4. keyboard.py - Text input and hardware button control

    • Type text (fast or slow)
    • Special keys (return, delete, tab, space, arrows)
    • Hardware buttons (home, lock, volume, screenshot)
    • Key combinations
    • Options: --type, --key, --button, --slow, --clear, --dismiss, --json
  5. app_launcher.py - App lifecycle management

    • Launch apps by bundle ID
    • Terminate apps
    • Install/uninstall from .app bundles
    • Deep link navigation
    • List installed apps
    • Check app state
    • Pass launch arguments (--args) and environment variables (--env KEY=VALUE, injected as SIMCTL_CHILD_*) to the app on launch/restart
    • Options: --launch, --terminate, --restart, --install, --uninstall, --open-url, --list, --state, --args, --env, --wait-for-debugger
Testing & Analysis (9 scripts)
  1. accessibility_audit.py - Check WCAG compliance on current screen

    • Critical issues (missing labels, empty buttons, no alt text)
    • Warnings (missing hints, small touch targets)
    • Info (missing IDs, deep nesting)
    • Options: --verbose, --output, --json
  2. visual_diff.py - Compare two screenshots for visual changes

    • Pixel-by-pixel comparison
    • Threshold-based pass/fail
    • Generate diff images
    • Options: --threshold, --output, --details, --json
  3. test_recorder.py - Automatically document test execution

    • Capture screenshots and accessibility trees per step
    • Generate markdown reports with timing data
    • Options: --test-name, --output, --verbose, --json
  4. app_state_capture.py - Create comprehensive debugging snapshots

    • Screenshot, UI hierarchy, app logs, device info
    • Markdown summary for bug reports
    • Options: --app-bundle-id, --output, --log-lines, --json
  5. sim_health_check.sh - Verify environment is properly configured

    • Check macOS, Xcode, simctl, IDB, Python
    • List available and booted simulators
    • Verify Python packages (Pillow)
  6. model_inspector.py - Inspect Core Data and SwiftData models from project files

    • Parse .xcdatamodeld packages (entities, attributes, relationships)
    • Detect model versions and current active version
    • Best-effort SwiftData @Model class extraction
    • Raw source dump for any model on demand (--raw ModelName)
    • Options: --project-path, --core-data-only, --swiftdata-only, --show-versions, --raw, --verbose, --json
  7. container.py - Inspect app sandbox: files, UserDefaults, and Core Data store paths

    • List data container files at configurable depth via --ls
    • Read files with auto-detected plist decoding via --cat (large files cached)
    • Dump UserDefaults as key=value or JSON via --userdefaults
    • Locate .sqlite / .sqlite-wal / .sqlite-shm stores via --core-data-path
    • Export full container snapshot via --export
    • Options: --ls, --cat, --userdefaults, --core-data-path, --export, --udid, --json, --verbose
  8. hang_watcher.py (HangBuster) - Record + summarise os_log hang events with progressive disclosure

    • Session mode (HangBuster, agent-native): start a detached recorder, interact with the simulator, stop for a token-tight summary
      • --start → returns a session ID; detached worker normalises + thresholds events on the fly
      • --stop SESSION_ID → emits ~80–120 token L1 summary (header + top-N clusters + drill hint)
      • --get-details SESSION_ID [--cluster N | --raw] → L2 full clusters or L3 per-event detail
      • --list-sessions / --clear-sessions [--older-than 24h] / --diff A B (cross-session regression report)
      • Filter pipeline: parse → normalise → threshold → bucket → cluster → aggregate → rank → format (in common/hang_pipeline.py)
      • --budget-tokens N picks the densest level (L0/L1/L2) that fits; --terse forces L0
      • --auto-sample captures a main-thread stack on first event per cluster (soft dependency: main_thread_sampler.py #62; graceful no-op if absent)
    • Raw capture mode (full fidelity for jq exploration): skip the clustering pipeline, dump every matching log line verbatim to raw.ndjson
      • --start --raw-capture [--max-size-mb 10] [--no-gzip] — spawns log stream --style ndjson
      • Per-session size cap (--max-size-mb, default 10) — worker stops cleanly on cap; extras.truncated=true
      • --stop gzips raw.ndjson → raw.ndjson.gz (~15–19× compression; --no-gzip opts out)
      • --get-details SESSION_ID on a raw session prints the path with a zcat | jq ... hint
    • Resilience (auto-restart on stream death): EOF or subprocess death triggers a stream_died event then a bounded restart with 2s backoff. After IOS_SIM_HANG_MAX_RESTARTS (default 3) the session is marked crashed, never left in stale running state. --list-sessions shows capture=Xs and restarts=N.
    • Cleanup is automatic: TTL prune (IOS_SIM_HANG_SESSION_TTL_HOURS, default 24h) + aggregate cap (IOS_SIM_HANG_TOTAL_CAP_MB, default 100 MB, oldest-first eviction) both run on every --start.
    • Legacy modes (unchanged for backward compat): --watch [--duration N] (live stream) and --since 5m (historical)
    • Filters: --bundle-id (post-parse — hang capture stays simulator-global so RunningBoard/SpringBoard events are kept), --predicate (also via IOS_SIM_HANG_PREDICATE)
    • All output supports --json; session storage at ~/.ios-simulator-skill/sessions/<id>/{meta.json,events.jsonl,summary.json,raw.ndjson.gz}

    Quick start (summarised mode):

    SID=$(python scripts/hang_watcher.py --start --min-hang-ms 200)
    # ... interact with the simulator (open sheets, scroll, navigate) ...
    python scripts/hang_watcher.py --stop $SID                  # token-tight L1 summary
    python scripts/hang_watcher.py --get-details $SID --cluster 1  # drill into cluster 1
    python scripts/hang_watcher.py --diff $SID_BASELINE $SID    # cross-session regression
    

    Quick start (raw capture + jq exploration):

    SID=$(python scripts/hang_watcher.py --start --raw-capture --max-size-mb 5)
    # ... interact with the simulator ...
    python scripts/hang_watcher.py --stop $SID
    # → "Session ...: raw mode, 737 lines, 0.96 MB → 0.05 MB gzipped"
    
    # Top processes by event count:
    zcat ~/.ios-simulator-skill/sessions/$SID/raw.ndjson.gz \
      | jq -s 'group_by(.processImagePath) | map({proc: (.[0].processImagePath | split("/") | last), n: length}) | sort_by(-.n) | .[:5]'
    
    # All RunningBoard assertion invalidations:
    zcat .../raw.ndjson.gz | jq -c 'select(.subsystem == "com.apple.runningboard" and (.eventMessage | startswith("Invalidating")))'
    
    # Hangs per minute:
    zcat .../raw.ndjson.gz | jq -r '.timestamp[:16]' | sort | uniq -c
    
  9. localization_audit.py - Detect string catalog gaps, missing keys, and placeholder mismatches

    • Report missing and needs_review/new keys per locale in .xcstrings catalogs
    • Cross-reference catalog keys against Swift source (String(localized:) / NSLocalizedString) via --source
    • Flag placeholder count mismatches (%d, %@, %s, %lld) across locales
    • Legacy .strings and .stringsdict support via plistlib
    • CI-friendly --strict exits 2 on any finding
    • Options: --catalog, --source, --locale, --strict, --json, --verbose
Advanced Testing & Permissions (4 scripts)
  1. clipboard.py - Manage simulator clipboard for paste testing

    • Copy text to clipboard
    • Test paste flows without manual entry
    • Options: --copy, --test-name, --expected, --json
  2. status_bar.py - Override simulator status bar appearance

    • Presets: clean (9:41, 100% battery), testing (11:11, 50%), low-battery (20%), airplane (offline)
    • Custom time, network, battery, WiFi settings
    • Options: --preset, --time, --data-network, --battery-level, --clear, --json
  3. push_notification.py - Send simulated push notifications

    • Simple mode (title + body + badge)
    • Custom JSON payloads
    • Test notification handling and deep links
    • Options: --bundle-id, --title, --body, --badge, --payload, --json
  4. privacy_manager.py - Grant, revoke, and reset app permissions

    • 13 supported services (camera, microphone, location, contacts, photos, calendar, health, etc.)
    • Batch operations (comma-separated services)
    • Audit trail with test scenario tracking
    • Options: --bundle-id, --grant, --revoke, --reset, --list, --json
Simulator Discovery (2 scripts)
  1. sim_list.py - List simulators with progressive disclosure

    • Concise summary by default (total / available / booted)
    • Full details on demand via cache IDs
    • Filter by device type
    • Suggest recommended simulators with --suggest
    • 96% token reduction vs raw simctl list (57k → 2k tokens)
    • Options: --get-details, --suggest, --device-type, --json
  2. simulator_selector.py - Suggest the best simulator for the job

    • Ranks candidates by recent use (from config.json), latest iOS, common test models, and boot status
    • List all available simulators with --list
    • Boot a selected simulator directly with --boot
    • JSON output for programmatic use
    • Options: --suggest, --list, --boot, --json
Device Lifecycle Management (5 scripts)
  1. simctl_boot.py - Boot simulators with optional readiness verification

    • Boot by UDID or device name
    • Wait for device ready with timeout
    • Batch boot operations (--all, --type)
    • Performance timing
    • Options: --udid, --name, --wait-ready, --timeout, --all, --type, --json
  2. simctl_shutdown.py - Gracefully shutdown simulators

    • Shutdown by UDID or device name
    • Optional verification of shutdown completion
    • Batch shutdown operations
    • Options: --udid, --name, --verify, --timeout, --all, --type, --json
  3. simctl_create.py - Create simulators dynamically

    • Create by device type and iOS version
    • List available device types and runtimes
    • Custom device naming
    • Returns UDID for CI/CD integration
    • Options: --device, --runtime, --name, --list-devices, --list-runtimes, --json
  4. simctl_delete.py - Permanently delete simulators

    • Delete by UDID or device name
    • Safety confirmation by default (skip with --yes)
    • Batch delete operations
    • Smart deletion (--old N to keep N per device type)
    • Options: --udid, --name, --yes, --all, --type, --old, --json
  5. simctl_erase.py - Factory reset simulators without deletion

    • Preserve device UUID (faster than delete+create)
    • Erase all, by type, or booted simulators
    • Optional verification
    • Options: --udid, --name, --verify, --timeout, --all, --type, --booted, --json

Common Patterns

Auto-UDID Detection: Most scripts auto-detect the booted simulator if --udid is not provided.

Device Name Resolution: Use device names (e.g., "iPhone 16 Pro") instead of UDIDs - scripts resolve automatically.

Batch Operations: Many scripts support --all for all simulators or --type iPhone for device type filtering.

Output Formats: Default is concise human-readable output. Use --json for machine-readable output in CI/CD.

Help: All scripts support --help for detailed options and examples.

Screenshot Sizing: Screenshots are resized to save tokens. Presets: full (3-4 tiles, ~5K tokens), half (1 tile, ~1.6K tokens, default), quarter (1 tile, ~800 tokens, less detail). Use quarter for quick visual checks, half for readable UI, full only when pixel-level detail matters. Scripts that capture screenshots (app_state_capture.py, test_recorder.py) default to half.

Typical Workflow

  1. Verify environment: bash scripts/sim_health_check.sh
  2. Launch app: python scripts/app_launcher.py --launch com.example.app
  3. Analyze screen: python scripts/screen_mapper.py
  4. Interact: python scripts/navigator.py --find-text "Button" --tap
  5. Verify: python scripts/accessibility_audit.py
  6. Debug if needed: python scripts/app_state_capture.py --app-bundle-id com.example.app

Configuration

Most operational limits can be tuned via environment variables. Defaults work for typical local development; raise them for slow CI runners, large monorepo builds, or accessibility audits on complex screens.

Variable Default Controls
IOS_SIM_A11Y_LABEL_MAX 80 Max chars of AXLabel retained in accessibility audit output
IOS_SIM_A11Y_TOP_ISSUES 10 Top accessibility issues surfaced per audit
IOS_SIM_APPS_PREVIEW 30 App entries listed by app_launcher.py before truncation
IOS_SIM_BOOT_SUBPROCESS_TIMEOUT 60 Timeout for the simctl boot subprocess itself (seconds)
IOS_SIM_BOOT_TIMEOUT 300 Wait-for-ready timeout after boot (seconds)
IOS_SIM_BUILD_JSON_CAP 50 Max build errors / failed tests in JSON output
IOS_SIM_BUILD_LOG_PREVIEW 4000 Chars of build log preview in default output
IOS_SIM_BUILD_TIMEOUT 1800 Max seconds for an xcodebuild build invocation before kill
IOS_SIM_INTROSPECT_TIMEOUT 60 Timeout for xcodebuild -list and simctl list lookups (seconds)
IOS_SIM_TEST_TIMEOUT 2700 Max seconds for an xcodebuild test invocation before kill
IOS_SIM_BUILD_SUMMARY_CAP 15 Errors/failures in default build summary
IOS_SIM_BUILD_VERBOSE_CAP 100 Errors/warnings in verbose build output
IOS_SIM_CACHE_MAX_ENTRIES 500 Max entries in progressive disclosure cache (LRU eviction)
IOS_SIM_CACHE_TTL_HOURS 1 Cache entry expiration
IOS_SIM_ERASE_TIMEOUT 90 Wait-for-erase timeout (seconds)
IOS_SIM_HANG_PREDICATE (default) Override the os_log predicate used by hang_watcher.py (default catches RunningBoard kills + "Hang detected" + main-thread hangs). Hang events originate from system daemons (RunningBoard, SpringBoard) so the predicate stays simulator-global — --bundle-id is applied post-parse, not ANDed in.
IOS_SIM_HANG_MIN_MS 250 HangBuster threshold — events below this duration never reach disk (smaller = more sensitive, larger summaries)
IOS_SIM_HANG_SESSION_TTL_HOURS 24 HangBuster session prune age; pruning runs on every --start
IOS_SIM_HANG_DEFAULT_TOP_N 3 Default top-N clusters in --stop L1 output
IOS_SIM_HANG_BUDGET_TOKENS (unset) Default token budget for --stop (picks L0/L1/L2 to fit)
IOS_SIM_HANG_MAX_RESTARTS 3 HangBuster worker: max log stream respawn attempts on EOF/subprocess death before the session is marked crashed
IOS_SIM_HANG_TOTAL_CAP_MB 100 HangBuster aggregate disk cap. When total session-state exceeds this on --start, oldest sessions are dropped first. Set to 0 to disable.
IOS_SIM_LOG_JSON_CAP 100 Max errors/warnings in log_monitor.py JSON output
IOS_SIM_LOG_LINE_MAX 300 Per-line truncation in log summaries
IOS_SIM_LOG_TAIL 200 Lines of log tail in verbose / sample output
IOS_SIM_LOG_TEXT_SUMMARY 15 Errors/warnings shown in text-mode log summary
IOS_SIM_MAX_ELEMENTS 25 Tappable elements listed by navigator.py
IOS_SIM_POLL_INTERVAL 0.5 Boot/erase state polling interval (seconds)
IOS_SIM_RELAUNCH_DELAY_MS 1000 Delay between terminate and re-launch in app_launcher.py
IOS_SIM_SCREEN_BUTTONS_PREVIEW 15 Button names listed by screen_mapper.py
IOS_SIM_SCREEN_SECTION_ITEMS 10 Items per section shown by screen_mapper.py
IOS_SIM_STATE_SUBPROCESS_TIMEOUT 15 Subprocess timeout in app_state_capture.py (seconds)
IOS_SIM_TAP_SETTLE_MS 500 Post-tap settle delay in navigator.py

Example:

# Slow GitHub Actions runner: give boot 10 minutes
IOS_SIM_BOOT_TIMEOUT=600 python scripts/simctl_boot.py --wait-ready

Requirements

  • macOS 15 (Sequoia)+
  • Xcode 26+ and Command Line Tools
  • Python 3.12+
  • idb 1.5.1+ - required for every interactive script (tap, swipe, type): brew tap facebook/fb && brew install facebook/fb/idb-companion facebook/fb/idb-cli
  • Pillow, for visual diffs only: pip3 install pillow

Verify with bash scripts/sim_health_check.sh (add --json for structured output).

Troubleshooting

Taps, swipes and typing do nothing, but reads work. idb reports success and the screen never changes. On Xcode 27 this means idb-companion is older than 1.5.1: it looks for SimulatorKit.framework at the path Xcode 26 used. Upgrade: brew upgrade facebook/fb/idb-companion.

SimulatorKit is required for HID interactions. Same cause, same fix.

Connection refused or No such file from every idb call. A dead companion is still in idb's registry at /tmp/idb/state, so idb dials a socket nobody is listening on instead of starting a fresh companion. Fix: idb disconnect <udid>.

open -a Simulator fails. Xcode 27 has no Simulator.app; it was replaced by DeviceHub.app in Xcode.app/Contents/Applications/. Boot headlessly instead: xcrun simctl boot <udid>. Note that quitting DeviceHub shuts down the simulator it hosts.

idb: command not found. The companion and the CLI are separate packages; install both (see Requirements). If which -a idb shows more than one, the first on PATH wins.

Documentation

  • SKILL.md (this file) - Script reference, requirements, troubleshooting
  • README.md - Installation, updating idb, Xcode 27 notes
  • CLAUDE.md - Architecture and implementation details

Key Design Principles

Semantic Navigation: Find elements by meaning (text, type, ID) not pixel coordinates. Survives UI changes.

Token Efficiency: Concise default output (3-5 lines) with optional verbose and JSON modes for detailed results.

Accessibility-First: Built on standard accessibility APIs for reliability and compatibility.

Zero Configuration: Works immediately on any macOS with Xcode. No setup required.

Structured Data: Scripts output JSON or formatted text, not raw logs. Easy to parse and integrate.

Auto-Learning: Build system remembers your device preference. Configuration stored per-project.


Use these scripts directly or let Claude Code invoke them automatically when your request matches the skill description.

1---
2name: ios-simulator-skill
3version: 1.5.0
4description: 29 production-ready scripts for iOS app testing, building, and automation. Provides semantic UI navigation, build automation, accessibility testing, and simulator lifecycle management. Optimized for AI agents with minimal token output.
5---
6 
7# iOS Simulator Skill
8 
9Build, test, and automate iOS applications using accessibility-driven navigation and structured data instead of pixel coordinates.
10 
11## Quick Start
12 
13```bash
14# 1. Check environment
15bash scripts/sim_health_check.sh
16 
17# 2. Launch app
18python scripts/app_launcher.py --launch com.example.app
19 
20# 3. Map screen to see elements
21python scripts/screen_mapper.py
22 
23# 4. Tap button
24python scripts/navigator.py --find-text "Login" --tap
25 
26# 5. Enter text
27python scripts/navigator.py --find-type TextField --enter-text "[email protected]"
28```
29 
30All scripts support `--help` for detailed options and `--json` for machine-readable output.
31 
32## Navigation Strategy
33 
34**Always prefer the accessibility tree over screenshots for navigation.** The accessibility tree gives you element types, labels, frames, and tap targets — structured data that's cheaper and more reliable than image analysis.
35 
36Use this priority:
371. `screen_mapper.py` → structured element list (5-7 lines, ~10 tokens)
382. `navigator.py --find-text/--find-type/--find-id` → semantic interaction
393. Screenshots → only for visual verification, bug reports, or visual diff
40 
41Screenshots cost 1,600–6,300 tokens depending on size. The accessibility tree costs 10–50 tokens in default mode.
42 
43## 29 Production Scripts
44 
45### Build & Development (2 scripts)
46 
471. **build_and_test.py** - Build Xcode projects, run tests, parse results with progressive disclosure
48 - Build with live result streaming
49 - Parse errors and warnings from xcresult bundles
50 - Retrieve detailed build logs on demand
51 - Options: `--project`, `--scheme`, `--clean`, `--test`, `--verbose`, `--json`
52 
532. **log_monitor.py** - Real-time log monitoring with intelligent filtering
54 - Stream logs or capture by duration
55 - Filter by severity (error/warning/info/debug)
56 - Deduplicate repeated messages
57 - Options: `--app`, `--severity`, `--follow`, `--duration`, `--output`, `--json`
58 
59### Device State (2 scripts)
60 
613. **appearance.py** - Control simulator appearance: dark mode, Dynamic Type size, and locale/region
62 - Toggle light/dark theme via `xcrun simctl ui`
63 - Set Dynamic Type size with friendly aliases (XS through AX5)
64 - Write locale and region defaults; optional app restart via `--bundle-id`
65 - RTL flagged automatically for ar/he/fa/ur/yi locales
66 - Options: `--theme`, `--text-size`, `--locale`, `--region`, `--reset`, `--bundle-id`, `--udid`, `--json`, `--verbose`
67 
684. **location.py** - Simulate GPS coordinates, named city presets, and GPX scenario playback
69 - Fix a coordinate with `--lat`/`--lng` or pick a city with `--city`
70 - Play a built-in scenario (City Run, Freeway Drive, etc.) via `--gpx <scenario>`
71 - Animate multi-waypoint paths with configurable speed via `--waypoints` and `--speed`
72 - Clear simulated location with `--clear`; list available scenarios with `--list-scenarios`
73 - Options: `--lat`, `--lng`, `--city`, `--gpx`, `--waypoints`, `--speed`, `--clear`, `--list-scenarios`, `--udid`, `--json`, `--verbose`
74 
75### Navigation & Interaction (5 scripts)
76 
775. **screen_mapper.py** - Analyze current screen and list interactive elements
78 - Element type breakdown
79 - Interactive button list
80 - Text field status
81 - Options: `--verbose`, `--hints`, `--json`
82 
836. **navigator.py** - Find and interact with elements semantically
84 - Find by text (fuzzy matching)
85 - Find by element type
86 - Find by accessibility ID
87 - Enter text or tap elements
88 - Options: `--find-text`, `--find-type`, `--find-id`, `--tap`, `--enter-text`, `--json`
89 
907. **gesture.py** - Perform swipes, scrolls, pinches, and complex gestures
91 - Directional swipes (up/down/left/right)
92 - Multi-swipe scrolling
93 - Pinch zoom
94 - Long press
95 - Pull to refresh
96 - Options: `--swipe`, `--scroll`, `--pinch`, `--long-press`, `--refresh`, `--json`
97 
988. **keyboard.py** - Text input and hardware button control
99 - Type text (fast or slow)
100 - Special keys (return, delete, tab, space, arrows)
101 - Hardware buttons (home, lock, volume, screenshot)
102 - Key combinations
103 - Options: `--type`, `--key`, `--button`, `--slow`, `--clear`, `--dismiss`, `--json`
104 
1059. **app_launcher.py** - App lifecycle management
106 - Launch apps by bundle ID
107 - Terminate apps
108 - Install/uninstall from .app bundles
109 - Deep link navigation
110 - List installed apps
111 - Check app state
112 - Pass launch arguments (`--args`) and environment variables (`--env KEY=VALUE`, injected as `SIMCTL_CHILD_*`) to the app on launch/restart
113 - Options: `--launch`, `--terminate`, `--restart`, `--install`, `--uninstall`, `--open-url`, `--list`, `--state`, `--args`, `--env`, `--wait-for-debugger`
114 
115### Testing & Analysis (9 scripts)
116 
11710. **accessibility_audit.py** - Check WCAG compliance on current screen
118 - Critical issues (missing labels, empty buttons, no alt text)
119 - Warnings (missing hints, small touch targets)
120 - Info (missing IDs, deep nesting)
121 - Options: `--verbose`, `--output`, `--json`
122 
12311. **visual_diff.py** - Compare two screenshots for visual changes
124 - Pixel-by-pixel comparison
125 - Threshold-based pass/fail
126 - Generate diff images
127 - Options: `--threshold`, `--output`, `--details`, `--json`
128 
12912. **test_recorder.py** - Automatically document test execution
130 - Capture screenshots and accessibility trees per step
131 - Generate markdown reports with timing data
132 - Options: `--test-name`, `--output`, `--verbose`, `--json`
133 
13413. **app_state_capture.py** - Create comprehensive debugging snapshots
135 - Screenshot, UI hierarchy, app logs, device info
136 - Markdown summary for bug reports
137 - Options: `--app-bundle-id`, `--output`, `--log-lines`, `--json`
138 
13914. **sim_health_check.sh** - Verify environment is properly configured
140 - Check macOS, Xcode, simctl, IDB, Python
141 - List available and booted simulators
142 - Verify Python packages (Pillow)
143 
14415. **model_inspector.py** - Inspect Core Data and SwiftData models from project files
145 - Parse .xcdatamodeld packages (entities, attributes, relationships)
146 - Detect model versions and current active version
147 - Best-effort SwiftData @Model class extraction
148 - Raw source dump for any model on demand (`--raw ModelName`)
149 - Options: `--project-path`, `--core-data-only`, `--swiftdata-only`, `--show-versions`, `--raw`, `--verbose`, `--json`
150 
15116. **container.py** - Inspect app sandbox: files, UserDefaults, and Core Data store paths
152 - List data container files at configurable depth via `--ls`
153 - Read files with auto-detected plist decoding via `--cat` (large files cached)
154 - Dump UserDefaults as key=value or JSON via `--userdefaults`
155 - Locate `.sqlite` / `.sqlite-wal` / `.sqlite-shm` stores via `--core-data-path`
156 - Export full container snapshot via `--export`
157 - Options: `--ls`, `--cat`, `--userdefaults`, `--core-data-path`, `--export`, `--udid`, `--json`, `--verbose`
158 
15917. **hang_watcher.py** (HangBuster) - Record + summarise os_log hang events with progressive disclosure
160 - **Session mode (HangBuster, agent-native):** start a detached recorder, interact with the simulator, stop for a token-tight summary
161 - `--start` → returns a session ID; detached worker normalises + thresholds events on the fly
162 - `--stop SESSION_ID` → emits ~80–120 token L1 summary (header + top-N clusters + drill hint)
163 - `--get-details SESSION_ID [--cluster N | --raw]` → L2 full clusters or L3 per-event detail
164 - `--list-sessions` / `--clear-sessions [--older-than 24h]` / `--diff A B` (cross-session regression report)
165 - Filter pipeline: parse → normalise → threshold → bucket → cluster → aggregate → rank → format (in `common/hang_pipeline.py`)
166 - `--budget-tokens N` picks the densest level (L0/L1/L2) that fits; `--terse` forces L0
167 - `--auto-sample` captures a main-thread stack on first event per cluster (soft dependency: `main_thread_sampler.py` #62; graceful no-op if absent)
168 - **Raw capture mode (full fidelity for `jq` exploration):** skip the clustering pipeline, dump every matching log line verbatim to `raw.ndjson`
169 - `--start --raw-capture [--max-size-mb 10] [--no-gzip]` — spawns `log stream --style ndjson`
170 - Per-session size cap (`--max-size-mb`, default 10) — worker stops cleanly on cap; `extras.truncated=true`
171 - `--stop` gzips `raw.ndjson` → `raw.ndjson.gz` (~15–19× compression; `--no-gzip` opts out)
172 - `--get-details SESSION_ID` on a raw session prints the path with a `zcat | jq ...` hint
173 - **Resilience (auto-restart on stream death):** EOF or subprocess death triggers a `stream_died` event then a bounded restart with 2s backoff. After `IOS_SIM_HANG_MAX_RESTARTS` (default 3) the session is marked `crashed`, never left in stale `running` state. `--list-sessions` shows `capture=Xs` and `restarts=N`.
174 - **Cleanup is automatic:** TTL prune (`IOS_SIM_HANG_SESSION_TTL_HOURS`, default 24h) + aggregate cap (`IOS_SIM_HANG_TOTAL_CAP_MB`, default 100 MB, oldest-first eviction) both run on every `--start`.
175 - **Legacy modes (unchanged for backward compat):** `--watch [--duration N]` (live stream) and `--since 5m` (historical)
176 - Filters: `--bundle-id` (post-parse — hang capture stays simulator-global so RunningBoard/SpringBoard events are kept), `--predicate` (also via `IOS_SIM_HANG_PREDICATE`)
177 - All output supports `--json`; session storage at `~/.ios-simulator-skill/sessions/<id>/{meta.json,events.jsonl,summary.json,raw.ndjson.gz}`
178 
179 **Quick start (summarised mode):**
180 ```bash
181 SID=$(python scripts/hang_watcher.py --start --min-hang-ms 200)
182 # ... interact with the simulator (open sheets, scroll, navigate) ...
183 python scripts/hang_watcher.py --stop $SID # token-tight L1 summary
184 python scripts/hang_watcher.py --get-details $SID --cluster 1 # drill into cluster 1
185 python scripts/hang_watcher.py --diff $SID_BASELINE $SID # cross-session regression
186 ```
187 
188 **Quick start (raw capture + `jq` exploration):**
189 ```bash
190 SID=$(python scripts/hang_watcher.py --start --raw-capture --max-size-mb 5)
191 # ... interact with the simulator ...
192 python scripts/hang_watcher.py --stop $SID
193 # → "Session ...: raw mode, 737 lines, 0.96 MB → 0.05 MB gzipped"
194 
195 # Top processes by event count:
196 zcat ~/.ios-simulator-skill/sessions/$SID/raw.ndjson.gz \
197 | jq -s 'group_by(.processImagePath) | map({proc: (.[0].processImagePath | split("/") | last), n: length}) | sort_by(-.n) | .[:5]'
198 
199 # All RunningBoard assertion invalidations:
200 zcat .../raw.ndjson.gz | jq -c 'select(.subsystem == "com.apple.runningboard" and (.eventMessage | startswith("Invalidating")))'
201 
202 # Hangs per minute:
203 zcat .../raw.ndjson.gz | jq -r '.timestamp[:16]' | sort | uniq -c
204 ```
205 
20618. **localization_audit.py** - Detect string catalog gaps, missing keys, and placeholder mismatches
207 - Report missing and `needs_review`/`new` keys per locale in `.xcstrings` catalogs
208 - Cross-reference catalog keys against Swift source (`String(localized:)` / `NSLocalizedString`) via `--source`
209 - Flag placeholder count mismatches (`%d`, `%@`, `%s`, `%lld`) across locales
210 - Legacy `.strings` and `.stringsdict` support via `plistlib`
211 - CI-friendly `--strict` exits 2 on any finding
212 - Options: `--catalog`, `--source`, `--locale`, `--strict`, `--json`, `--verbose`
213 
214### Advanced Testing & Permissions (4 scripts)
215 
21619. **clipboard.py** - Manage simulator clipboard for paste testing
217 - Copy text to clipboard
218 - Test paste flows without manual entry
219 - Options: `--copy`, `--test-name`, `--expected`, `--json`
220 
22120. **status_bar.py** - Override simulator status bar appearance
222 - Presets: clean (9:41, 100% battery), testing (11:11, 50%), low-battery (20%), airplane (offline)
223 - Custom time, network, battery, WiFi settings
224 - Options: `--preset`, `--time`, `--data-network`, `--battery-level`, `--clear`, `--json`
225 
22621. **push_notification.py** - Send simulated push notifications
227 - Simple mode (title + body + badge)
228 - Custom JSON payloads
229 - Test notification handling and deep links
230 - Options: `--bundle-id`, `--title`, `--body`, `--badge`, `--payload`, `--json`
231 
23222. **privacy_manager.py** - Grant, revoke, and reset app permissions
233 - 13 supported services (camera, microphone, location, contacts, photos, calendar, health, etc.)
234 - Batch operations (comma-separated services)
235 - Audit trail with test scenario tracking
236 - Options: `--bundle-id`, `--grant`, `--revoke`, `--reset`, `--list`, `--json`
237 
238### Simulator Discovery (2 scripts)
239 
24023. **sim_list.py** - List simulators with progressive disclosure
241 - Concise summary by default (total / available / booted)
242 - Full details on demand via cache IDs
243 - Filter by device type
244 - Suggest recommended simulators with `--suggest`
245 - 96% token reduction vs raw `simctl list` (57k → 2k tokens)
246 - Options: `--get-details`, `--suggest`, `--device-type`, `--json`
247 
24824. **simulator_selector.py** - Suggest the best simulator for the job
249 - Ranks candidates by recent use (from `config.json`), latest iOS, common test models, and boot status
250 - List all available simulators with `--list`
251 - Boot a selected simulator directly with `--boot`
252 - JSON output for programmatic use
253 - Options: `--suggest`, `--list`, `--boot`, `--json`
254 
255### Device Lifecycle Management (5 scripts)
256 
25725. **simctl_boot.py** - Boot simulators with optional readiness verification
258 - Boot by UDID or device name
259 - Wait for device ready with timeout
260 - Batch boot operations (--all, --type)
261 - Performance timing
262 - Options: `--udid`, `--name`, `--wait-ready`, `--timeout`, `--all`, `--type`, `--json`
263 
26426. **simctl_shutdown.py** - Gracefully shutdown simulators
265 - Shutdown by UDID or device name
266 - Optional verification of shutdown completion
267 - Batch shutdown operations
268 - Options: `--udid`, `--name`, `--verify`, `--timeout`, `--all`, `--type`, `--json`
269 
27027. **simctl_create.py** - Create simulators dynamically
271 - Create by device type and iOS version
272 - List available device types and runtimes
273 - Custom device naming
274 - Returns UDID for CI/CD integration
275 - Options: `--device`, `--runtime`, `--name`, `--list-devices`, `--list-runtimes`, `--json`
276 
27728. **simctl_delete.py** - Permanently delete simulators
278 - Delete by UDID or device name
279 - Safety confirmation by default (skip with --yes)
280 - Batch delete operations
281 - Smart deletion (--old N to keep N per device type)
282 - Options: `--udid`, `--name`, `--yes`, `--all`, `--type`, `--old`, `--json`
283 
28429. **simctl_erase.py** - Factory reset simulators without deletion
285 - Preserve device UUID (faster than delete+create)
286 - Erase all, by type, or booted simulators
287 - Optional verification
288 - Options: `--udid`, `--name`, `--verify`, `--timeout`, `--all`, `--type`, `--booted`, `--json`
289 
290## Common Patterns
291 
292**Auto-UDID Detection**: Most scripts auto-detect the booted simulator if --udid is not provided.
293 
294**Device Name Resolution**: Use device names (e.g., "iPhone 16 Pro") instead of UDIDs - scripts resolve automatically.
295 
296**Batch Operations**: Many scripts support `--all` for all simulators or `--type iPhone` for device type filtering.
297 
298**Output Formats**: Default is concise human-readable output. Use `--json` for machine-readable output in CI/CD.
299 
300**Help**: All scripts support `--help` for detailed options and examples.
301 
302**Screenshot Sizing**: Screenshots are resized to save tokens. Presets: `full` (3-4 tiles, ~5K tokens), `half` (1 tile, ~1.6K tokens, default), `quarter` (1 tile, ~800 tokens, less detail). Use `quarter` for quick visual checks, `half` for readable UI, `full` only when pixel-level detail matters. Scripts that capture screenshots (`app_state_capture.py`, `test_recorder.py`) default to `half`.
303 
304## Typical Workflow
305 
3061. Verify environment: `bash scripts/sim_health_check.sh`
3072. Launch app: `python scripts/app_launcher.py --launch com.example.app`
3083. Analyze screen: `python scripts/screen_mapper.py`
3094. Interact: `python scripts/navigator.py --find-text "Button" --tap`
3105. Verify: `python scripts/accessibility_audit.py`
3116. Debug if needed: `python scripts/app_state_capture.py --app-bundle-id com.example.app`
312 
313## Configuration
314 
315Most operational limits can be tuned via environment variables. Defaults work for typical local development; raise them for slow CI runners, large monorepo builds, or accessibility audits on complex screens.
316 
317| Variable | Default | Controls |
318|---|---|---|
319| `IOS_SIM_A11Y_LABEL_MAX` | `80` | Max chars of `AXLabel` retained in accessibility audit output |
320| `IOS_SIM_A11Y_TOP_ISSUES` | `10` | Top accessibility issues surfaced per audit |
321| `IOS_SIM_APPS_PREVIEW` | `30` | App entries listed by `app_launcher.py` before truncation |
322| `IOS_SIM_BOOT_SUBPROCESS_TIMEOUT` | `60` | Timeout for the `simctl boot` subprocess itself (seconds) |
323| `IOS_SIM_BOOT_TIMEOUT` | `300` | Wait-for-ready timeout after boot (seconds) |
324| `IOS_SIM_BUILD_JSON_CAP` | `50` | Max build errors / failed tests in JSON output |
325| `IOS_SIM_BUILD_LOG_PREVIEW` | `4000` | Chars of build log preview in default output |
326| `IOS_SIM_BUILD_TIMEOUT` | `1800` | Max seconds for an `xcodebuild build` invocation before kill |
327| `IOS_SIM_INTROSPECT_TIMEOUT` | `60` | Timeout for `xcodebuild -list` and `simctl list` lookups (seconds) |
328| `IOS_SIM_TEST_TIMEOUT` | `2700` | Max seconds for an `xcodebuild test` invocation before kill |
329| `IOS_SIM_BUILD_SUMMARY_CAP` | `15` | Errors/failures in default build summary |
330| `IOS_SIM_BUILD_VERBOSE_CAP` | `100` | Errors/warnings in verbose build output |
331| `IOS_SIM_CACHE_MAX_ENTRIES` | `500` | Max entries in progressive disclosure cache (LRU eviction) |
332| `IOS_SIM_CACHE_TTL_HOURS` | `1` | Cache entry expiration |
333| `IOS_SIM_ERASE_TIMEOUT` | `90` | Wait-for-erase timeout (seconds) |
334| `IOS_SIM_HANG_PREDICATE` | _(default)_ | Override the `os_log` predicate used by `hang_watcher.py` (default catches RunningBoard kills + "Hang detected" + main-thread hangs). Hang events originate from system daemons (RunningBoard, SpringBoard) so the predicate stays simulator-global — `--bundle-id` is applied post-parse, not ANDed in. |
335| `IOS_SIM_HANG_MIN_MS` | `250` | HangBuster threshold — events below this duration never reach disk (smaller = more sensitive, larger summaries) |
336| `IOS_SIM_HANG_SESSION_TTL_HOURS` | `24` | HangBuster session prune age; pruning runs on every `--start` |
337| `IOS_SIM_HANG_DEFAULT_TOP_N` | `3` | Default top-N clusters in `--stop` L1 output |
338| `IOS_SIM_HANG_BUDGET_TOKENS` | _(unset)_ | Default token budget for `--stop` (picks L0/L1/L2 to fit) |
339| `IOS_SIM_HANG_MAX_RESTARTS` | `3` | HangBuster worker: max `log stream` respawn attempts on EOF/subprocess death before the session is marked `crashed` |
340| `IOS_SIM_HANG_TOTAL_CAP_MB` | `100` | HangBuster aggregate disk cap. When total session-state exceeds this on `--start`, oldest sessions are dropped first. Set to `0` to disable. |
341| `IOS_SIM_LOG_JSON_CAP` | `100` | Max errors/warnings in `log_monitor.py` JSON output |
342| `IOS_SIM_LOG_LINE_MAX` | `300` | Per-line truncation in log summaries |
343| `IOS_SIM_LOG_TAIL` | `200` | Lines of log tail in verbose / sample output |
344| `IOS_SIM_LOG_TEXT_SUMMARY` | `15` | Errors/warnings shown in text-mode log summary |
345| `IOS_SIM_MAX_ELEMENTS` | `25` | Tappable elements listed by `navigator.py` |
346| `IOS_SIM_POLL_INTERVAL` | `0.5` | Boot/erase state polling interval (seconds) |
347| `IOS_SIM_RELAUNCH_DELAY_MS` | `1000` | Delay between terminate and re-launch in `app_launcher.py` |
348| `IOS_SIM_SCREEN_BUTTONS_PREVIEW` | `15` | Button names listed by `screen_mapper.py` |
349| `IOS_SIM_SCREEN_SECTION_ITEMS` | `10` | Items per section shown by `screen_mapper.py` |
350| `IOS_SIM_STATE_SUBPROCESS_TIMEOUT` | `15` | Subprocess timeout in `app_state_capture.py` (seconds) |
351| `IOS_SIM_TAP_SETTLE_MS` | `500` | Post-tap settle delay in `navigator.py` |
352 
353Example:
354 
355```bash
356# Slow GitHub Actions runner: give boot 10 minutes
357IOS_SIM_BOOT_TIMEOUT=600 python scripts/simctl_boot.py --wait-ready
358```
359 
360## Requirements
361 
362- macOS 15 (Sequoia)+
363- Xcode 26+ and Command Line Tools
364- Python 3.12+
365- `idb` **1.5.1+** - required for every interactive script (tap, swipe, type):
366 `brew tap facebook/fb && brew install facebook/fb/idb-companion facebook/fb/idb-cli`
367- Pillow, for visual diffs only: `pip3 install pillow`
368 
369Verify with `bash scripts/sim_health_check.sh` (add `--json` for structured output).
370 
371## Troubleshooting
372 
373**Taps, swipes and typing do nothing, but reads work.** `idb` reports success and the screen
374never changes. On Xcode 27 this means `idb-companion` is older than 1.5.1: it looks for
375`SimulatorKit.framework` at the path Xcode 26 used. Upgrade:
376`brew upgrade facebook/fb/idb-companion`.
377 
378**`SimulatorKit is required for HID interactions`.** Same cause, same fix.
379 
380**`Connection refused` or `No such file` from every idb call.** A dead companion is still in
381idb's registry at `/tmp/idb/state`, so idb dials a socket nobody is listening on instead of
382starting a fresh companion. Fix: `idb disconnect <udid>`.
383 
384**`open -a Simulator` fails.** Xcode 27 has no `Simulator.app`; it was replaced by
385`DeviceHub.app` in `Xcode.app/Contents/Applications/`. Boot headlessly instead:
386`xcrun simctl boot <udid>`. Note that quitting DeviceHub shuts down the simulator it hosts.
387 
388**`idb: command not found`.** The companion and the CLI are separate packages; install both
389(see Requirements). If `which -a idb` shows more than one, the first on `PATH` wins.
390 
391## Documentation
392 
393- **SKILL.md** (this file) - Script reference, requirements, troubleshooting
394- **README.md** - Installation, updating idb, Xcode 27 notes
395- **CLAUDE.md** - Architecture and implementation details
396 
397## Key Design Principles
398 
399**Semantic Navigation**: Find elements by meaning (text, type, ID) not pixel coordinates. Survives UI changes.
400 
401**Token Efficiency**: Concise default output (3-5 lines) with optional verbose and JSON modes for detailed results.
402 
403**Accessibility-First**: Built on standard accessibility APIs for reliability and compatibility.
404 
405**Zero Configuration**: Works immediately on any macOS with Xcode. No setup required.
406 
407**Structured Data**: Scripts output JSON or formatted text, not raw logs. Easy to parse and integrate.
408 
409**Auto-Learning**: Build system remembers your device preference. Configuration stored per-project.
410 
411---
412 
413Use these scripts directly or let Claude Code invoke them automatically when your request matches the skill description.
414 

Discussion

Alternatives