xcui Reference (Scriptable Simulator UI & Accessibility Testing)
Complete reference for xcui, the Axiom-bundled CLI that makes iOS-simulator UI and accessibility testing scriptable for coding harnesses. It owns the test-harness semantics AXe and simctl lack — waiting on conditions, asserting on the accessibility tree, toggling accessibility settings, handling system permission dialogs, and computing VoiceOver announcements — and is also the front door for input: xcui tap/type/swipe forward to AXe verbatim, so you get its real HID touch without managing AXe's environment yourself. Every subcommand emits a single compact JSON object with a tool/version envelope (token-lean for LLM consumers); every subcommand also accepts --human for a prose rendering, and exit codes drive pass/fail in scripts.
SimulatorKit – prefer
xcui tapoveraxe tap: the forwarded verbs carry xcui'sDEVELOPER_DIRhandling, so they keep working under an Xcode that AXe cannot load on its own. The only calls left bare areaxe describe-uiand the streaming verbs; prefix those withDEVELOPER_DIR=<value>ifxcui doctorreports anaxe_developer_dir— rare, since AXe 1.8.0 finds the framework at its Xcode 27 home unaided.
When to Use This Reference
Use this reference when:
- Looking up
xcui doctor/wait/assert/a11y set/a11y reset/dialog/voiceoversubcommand flags - Interpreting an exit code (0 pass / 1 assertion-fail or wait-timeout / 2 environment error / 8 output-write error)
- Checking which accessibility toggles
a11y setsupports and how each is applied (nativesimctl uivsdefaults write+ relaunch) - Handling a system permission alert in a test — tapping the right button (
dialog accept/dismiss) or pre-granting so it never appears (dialog pregrant) - Validating VoiceOver announcements and focus order without capturing audio (
voiceover traverse/assert) - Understanding how
xcuiauto-resolves the booted simulator and when to pass--udid - Deciding what to drive with
xcuiversus what to call onaxedirectly (taps, typing, gestures) - Switching between JSON (default) and
--humanoutput - Diagnosing why a
doctorrun reports exit 2 (AXe missing or no booted sim)
Example Prompts
- "How do I wait for an element to appear on the simulator before asserting?"
- "How do I assert a VoiceOver label and trait on an element?"
- "How do I turn on Dynamic Type or Increase Contrast on the simulator?"
- "How do I dismiss the camera permission dialog in my test — or skip it entirely?"
- "How do I check the VoiceOver announcement order without listening to audio?"
- "What does
xcui doctorcheck, and how do I install AXe?" - "Why did
xcui assertexit 1?" - "Why isn't my accessibility toggle taking effect until I relaunch the app?"
What's Covered
- Invocation –
xcuiis on PATH as a bare command (pluginbin/is auto-resolved); runxcui <subcommand> doctorsubcommand – verifies AXe, Homebrew, Xcode, and a booted simulator;--installrunsbrew install cameroncooke/axe/axe(explicit/consented, never silent);--humanfor prose. Auto-resolves the booted sim (deterministic when several are booted — it picks the lowest UDID and adds anotelisting the others);--udid <id>reports a specific onewaitsubcommand –--for-element <id>,--gone <id>, or--idle, with--timeoutand--poll; polls the accessibility tree until the condition holds or the deadline passes (the headless equivalent ofwaitForExistence)assertsubcommand –--id <id>plus optional--label,--value,--trait, and--single;--singleasserts the id resolves to exactly one element;--traitmatches a bare word (button,image) against the AX role or typea11y set/a11y reset– the four verified toggles and how each is applied (see table below);--app <bundle-id>triggers an app relaunch for the toggles that need itdialogsubcommand –accept/dismissfind the frontmost system alert and tap the correct standard button (permission grants,OK,Cancel); a one-button alert is tapped for either intent; matching is case- and apostrophe-insensitive.pregrant <bundle-id> <service>…grants permissions viasimctl privacyso the dialog never appears. Exit0handled,1no actionable alertvoiceoversubcommand –traversewalks the tree in focus order and emits the computed announcement sequence (label, value, trait, plusdimmedwhen disabled);assert --sequence <file>compares the live sequence to an expected one and reports every differing index (plus a length-mismatch note when counts differ). This is computed from the accessibility tree, not captured TTS audio (which the simulator does not expose) — use it to catch missing labels, wrong traits, and bad focus order- Input verbs –
tap,slider,type,swipe,drag,touch,gesture,button,key,key-sequence,key-combo,screenshotforward to AXe verbatim (same flags, same output, same exit code), with--udidinjected when omitted;axe describe-uistays bare as the raw treexcuiparses resize sweepsubcommand (OS 27) – drive a resizable-app session across breakpoints and check each, in one JSON envelope: session lifecycle held and torn down (including on SIGINT/SIGTERM),--screenshot-dirfor a PNG per breakpoint,--assert-idfor an accessibility check,--strictto fail on a clamped size, actual-vs-requested reported with anhonoredflag, per-assertion retry for post-resize flakiness, and the threeappResizeCoreDeviceErrors (1001 / 24001 / 24004) separated into distinct fixes- Output envelope & exit codes – single compact JSON object with
tool/versionfirst; every subcommand accepts--humanfor prose; exit0pass ·1assertion-fail/wait-timeout ·2environment error ·8output-write error - CLI grammar gotcha – Go's flag parser stops at the first positional, so always use the all-flag forms (
assert --id …, notassert <id> …)
Accessibility Toggles
a11y set --toggle <name> --value <…> supports the following, all verified against the booted simulator. a11y reset clears them (deletes the defaults keys, sets content_size large, sets increase_contrast disabled).
| Toggle | Mechanism | --value | Relaunch |
|---|---|---|---|
dynamic-type | native simctl ui content_size | a size (large … accessibility-extra-extra-extra-large) | no |
increase-contrast | native simctl ui increase_contrast | on / off | no |
reduce-motion | defaults write com.apple.Accessibility ReduceMotionEnabled | on / off | yes (pass --app) |
reduce-transparency | defaults write com.apple.Accessibility ReduceTransparencyEnabled | on / off | yes (pass --app) |
voiceover | devicectl device settings voiceover | on / off | no |
differentiate-without-color and bold-text are still not supported — no native simctl ui setter, candidate defaults keys not honored on the sim, and devicectl device settings covers only appearance, audio, biometrics, reset, and voiceover. They are omitted rather than shipped unverified.
Turning VoiceOver on is not the same as xcui voiceover
a11y set --toggle voiceover starts the real screen reader. xcui voiceover traverse computes the announcement sequence from the accessibility tree with VoiceOver off — deterministic, no speech, no focus stealing, and the one you want in CI. Use the toggle only when you need the device in a genuine VoiceOver state.
Documentation Scope
This page documents the xcui-ref reference skill — the bundled Axiom CLI for scriptable simulator UI and accessibility testing.
- For the agent that drives
xcuilive (set toggles, wait, assert on the tree), see the simulator-tester agent - For static accessibility source scanning (the read-only counterpart that pairs with live validation), see the accessibility-auditor agent
- For the input primitives
xcuidelegates to, see AXe (Simulator Automation) - For device state setup (biometrics, orientation, location), that is
devicectl's job, notxcui's —xcuivalidates the on-screen result,devicectldrives the state - For the
/axiom:uicommand wrapper, see /axiom:ui - For the sibling bundled tools, see Console Capture (xclog) and Crash Symbolication (xcsym)