Skip to content

Reference

Device Control

The device control skill provides comprehensive reference for controlling simulators and emulators, capturing screenshots, reading UI state, managing permissions, and handling Expo/EAS builds.

Activates when Claude encounters tasks involving simulator/emulator interaction: taking screenshots, booting devices, installing apps, reading UI hierarchy, opening deep links, granting permissions, streaming native logs, or managing device settings.

Commands for boot/shutdown, app install/launch/terminate, deep links, screenshots, native log streaming, and permissions. Key recommendations:

  • Prefer JPEG screenshots (80ms, 200KB) over PNG (150ms, 800KB)
  • Use processImagePath ENDSWITH "/BinaryName" for precise native log filtering
  • Use xcrun simctl privacy for permission grants/revokes

Commands for device management, deep links, screenshots, UI hierarchy extraction, animation control, native logs, permissions, and locale. Key recommendations:

  • Use adb exec-out screencap -p for direct-pipe screenshots (skips device storage round-trip)
  • uiautomator dump --compressed provides full structured accessibility tree
  • Disable animations before test runs (window, transition, animator scales to 0)

Cross-platform unified device interaction that eliminates platform-specific branching. The device_* tools route through the in-tree runners (iOS: rn-fast-runner; Android: rn-android-runner) — the sole device backend:

TaskTool
List devicesdevice_list
Screenshotdevice_screenshot
UI element treedevice_snapshot (returns @refs)
Tap by textdevice_find text="Sign In" action=click
Tap by refdevice_press ref=@e3
Fill inputdevice_fill ref=@e5 text="hello"
Scroll/swipedevice_swipe direction=up
System backdevice_back

scripts/snapshot_state.sh captures screenshot + UI hierarchy simultaneously, cutting state-check time by ~40%.

OperationTimeSize
iOS screenshot (JPEG)80ms200KB
iOS screenshot (PNG)150ms800KB
Android screenshot (exec-out)300ms800KB
Android UI hierarchy (raw)300-500ms15-30KB XML
Android UI hierarchy (parsed)350-550ms2-3KB JSON

Decision table for build workflows:

SituationAction
App running + Metro connectedSkip, proceed to testing
Metro not running, app missingexpo_ensure_running.sh ios or android
Test a specific EAS buildeas_resolve_artifact.sh then expo_ensure_running.sh --artifact
SymptomFix
Simulator not bootingxcrun simctl shutdown all && xcrun simctl erase all
adb shows “unauthorized”Revoke USB debugging, reconnect, tap Allow
Screenshot hangsWait for home screen, verify boot completed
uiautomator dump failsWake screen with keyevent WAKEUP

device_press and device_longpress guard against tapping through a software keyboard by default. Before the tap lands, the runner checks whether a visible keyboard’s frame contains the tap point (a sanity-checked rect — min height 120pt iOS / 150px Android, so a predictive-text accessory bar doesn’t count) and dismisses it first if so.

meta.keyboardGuardMeaning
offGuard disabled (RN_KEYBOARD_GUARD=0)
no_keyboardNo keyboard was visible
not_occludedKeyboard visible but the tap point was clear of it
dismissedKeyboard was occluding the tap point and was dismissed first

Platform behavior differs because the safe dismissal mechanism differs:

  • AndroidpressBack + a bounded wait for the UI to settle (~3.6s measured). Only fires when a real input-method window with sane bounds contains the tap point, so it never triggers an unrelated back-navigation.
  • iOS — verify-or-refuse. The runner only ever taps a genuine dismiss control (“Hide keyboard” / “Dismiss keyboard” / “Done”) and re-verifies the keyboard is gone. On the standard iPhone QWERTY keyboard, which has no such control, the tap is refused with a KEYBOARD_OCCLUDED error (keyboardGuard=dismiss_failed) instead of swiping the keyboard closed — a swipe-based dismiss triggers QuickPath slide-typing and corrupts whatever the keyboard was over.

If you hit KEYBOARD_OCCLUDED on iOS: dismiss the keyboard yourself first (device_fill/cdp_interact type through the JS path and don’t require the keyboard to be down, or tap a non-input area of the screen) and then retry the tap.

Opt out entirely with RN_KEYBOARD_GUARD=0 (or false) if the guard gets in the way of a flow you control precisely.

Only device_press/device_longpress-style taps are guarded — tapSeries, text-based taps, swipes/scrolls/drags, and doubleTap are unaffected.