Connect an iPhone
Physical iPhone support is not reliable yet. Read the open problems first.
First-Time USB Preparation
- Connect the iPhone to your Mac over USB, unlock the phone, and accept Trust This Computer.
- On the iPhone, enable Developer Mode and turn on UI Automation in Developer Settings. Complete all on-device prompts manually.
- Ensure developer services are mounted before capturing screens or injecting input. If compatible Apple-signed Developer Disk Image files already exist on your Mac under
/Library/Developer/DeveloperDiskImages/iOS_DDI/Restore, run:
sh
node bin/prepare-device.mjs YOUR_DEVICE_ID(If exactly one device is connected, YOUR_DEVICE_ID can be omitted.) This helper checks device services and mounts the local image when needed; it does not download Apple files, alter security settings, or reboot the device.
Post-Pairing Wi-Fi Operation
Once paired over USB, the Mac and iPhone can communicate over the same local network for wireless discovery and supported operations while the phone stays unlocked and reachable:
- Verified wireless operations: Prior hardware testing (with USB disconnected) verified screenshots, accessibility field readback, standard key input, backward/forward deletion, Unicode paste, and verified paste followed by
Return. (Note: Those checks were not re-run as a full hardware pass after later input changes.) - Native app launch: Physical
launch_appandopen_settingswithout a page use the paired device’s RemoteXPC AppService directly. An explicit bundle ID skips application discovery; name lookup uses the advertised streaming app list. Arguments, environment, language, and locale are preserved. A positive process ID must match the observed foreground before success is reported. Foreground verification makes at most three observation attempts with a 2.5-second attempt limit and an 8-second total observation deadline; child-process cleanup completes before returning. Only observation is retried, never launch. If launch may have happened but cannot be confirmed, the receipt preserves known facts and stops even a permissive batch. - Application discovery: Physical
list_appsuses the paired device’s advertised streaming app list, including for shared name resolution. System filtering retains the application-path distinction. Container-access eligibility is not supplied by this list and is not inferred. - Wireless limitations: Application install/uninstall use paired InstallationProxy and verify the resulting app inventory. If an installation is interrupted, its private recovery record remains under the state directory at
install/operations/<UUID>.json, identifying the exact possible device staging path; it is never automatically retried or deleted while its status is uncertain. Remaining process/container management and Settings deep links still use Appledevicectl, which may fail to discover a wirelessly paired device (CoreDeviceError 1000) or reject a launch (4016). Native input and app launch do not prove those separate management paths work.
Open problems
The earlier measurements below come from a single iPhone running iOS 27.3 over Wi-Fi. Treat them as a test log from that setup, not a guarantee for other apps or devices.
- With the old connection handling, tapping a specific search field in a chat app dropped 2 of 70 touches. Every dropped touch still returned
sent: true. Keeping the connection open until the post-touch wait finished dropped 0 of 70 touches, and the final build dropped 0 of 24. These numbers only apply to that single search field. - Via MCP in the same app,
tap_texttook effect 12 of 12 times. Batches that launched the app and calledtap_textopened the conversation 14 of 14 times. - Via MCP,
fillon that search field worked 6 of 6 times and took about 15 seconds. Iffillcannot verify the entered text, it now returns an error and stops the batch, matching Simulator behavior.tap_texttook about 8 seconds. - Two earlier
tap_textcalls on a conversation row returnedsent: truebut had no effect. We have not reproduced this failure, so it may have a different cause. Why the device drops touches is unknown. - Touches sent too soon after app launch may have no effect. At 1.5 seconds after launch, 0 of 3 taps opened the conversation. At 2.8 seconds or later, 12 of 12 did. Via MCP, the pre-tap screen reads have so far delayed the tap past this window.
- Tapping a target next to a bottom floating bar had no effect 4 of 4 times in the Settings app. This is not fixed. Those taps now return
screen_changed: false. - We only measured taps.
dragandpinchuse the same connection path and were not measured. The latest update to howscreen_changedcompares screens has not been tested with touches on a physical phone. - In one landscape game, taps on button coordinates had no effect, while portrait taps in the same session worked. The cause is unknown.
How input reaches the device
- Input goes through a standalone Rust program in
native/bridge/, built on the pinnedidevicepublic protocol interfaces. sent=trueonly means the touch or key reports were written to the connection. The device does not acknowledge these reports, so a dropped report produces no error. Check the screen through accessibility or a screenshot.- Keyboard input registers a virtual keyboard over UniversalHID. Unicode text goes through the clipboard and Command-V, and the bridge never taps Allow Paste.