You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
CLI shape: make physical iOS devices a first-class target (listing, device-kind field, top-level + daemon routing) #115
Problem inventory before we design the CLI shape. This issue only catalogues the current state and the inconsistencies; the actual design is deferred to follow-up discussion.
The theme: sim-use ios-device is currently an appendage, not a first-class target. If we commit to physical iOS support, it should behave like every other target (with some capabilities legitimately restricted), rather than living off to the side.
Current state
1. sim-use devices listing coverage
Target
Listed by sim-use devices?
Source
iOS Simulator
✅
simctl list devices
iOS physical device
❌ blind spot
would need FBDeviceControl (what ios-device devices already uses)
Android emulator
✅
adb devices
Android physical device
✅
adb devices
simctl only knows simulators, so sim-use devices never shows a plugged-in iPhone — even though sim-use ios-device devices lists it fine through a different code path. Android, by contrast, shows both emulators and physical devices. So the listing is asymmetric: Android physical is visible, iOS physical is not.
2. No device-kind field
Device (the sim-use devices row / --json schema) carries platform ∈ {ios, android}, plus free-form state, runtime, name, deviceId. There is no field distinguishing simulator / emulator / physical:
Android emulator vs physical device is only inferable from the serial shape (emulator-5554 prefix), not a structured field.
iOS simulator vs physical has no field either (and physical isn't listed at all today).
The PLATFORM column answers "ios or android" but not "simulator, emulator, or real device", which is exactly the distinction that determines available capabilities.
3. Physical iOS is not routed like other targets
Surface
iOS Simulator
Android
iOS physical
Top-level verbs (ui, tap, screenshot, …)
✅ routed by UDID shape (PlatformRouter)
✅
❌ rejected — must use the ios-device namespace
Per-UDID daemon (auto-spawn, amortised init)
✅
✅
❌ — every ios-device call opens its own DTX session
Namespace
sim-use ios <verb> (iOS-only extras)
sim-use android <verb>
sim-use ios-device <verb> (the whole surface)
PlatformRouter has only .iOSSim / .android cases; a physical iOS UDID resolves to neither and top-level verbs reject it (pointing at ios-device).
The ios-device subcommands are plain AsyncParsableCommands that call DeviceSession.withClient directly — they do not implement SimUseExecutableCommand, so they bypass the daemon entirely.
This contradicts sim-use's stated design: "one command surface; the UDID shape decides the backend." A physical iOS device is the only target you address through a wholly separate command tree.
Problems to solve (design deferred)
Listing parity — sim-use devices should surface physical iOS devices (~~probably opt-in given FBDeviceControl discovery cost; see the ~5 s attachment-quiescence wait~~), so discovery is uniform across every target. (Edit 2026-08-27: the opt-in rationale is retracted — measured discovery is ~0.4 s with a device attached, and fix: bail fast from ios-device discovery when no device is attached #117 cut the no-device path to ~1 s, so default-on is plausible; see the measurement comments below.)
Device-kind field — add a structured field distinguishing simulator / emulator / physical (name TBD), orthogonal to platform. Lets the table and --json say "ios physical" vs "ios simulator" vs "android emulator" explicitly, and lets callers reason about which capabilities apply.
First-class routing — decide whether physical iOS joins PlatformRouter + top-level verb forwarding + the daemon, so sim-use ui / sim-use tap work against a plugged-in iPhone (with the restricted capability set), instead of requiring ios-device. This is the biggest question and where "first-class citizen vs restricted appendage" is actually decided.
Capability model — with physical iOS routed through the top level, unsupported verbs (coordinate tap, swipe, gesture, screenshot,--json, …) need a consistent "capability not available on this target" contract rather than today's hard namespace split. (Edit 2026-08-27: screenshot moved to the supported column — shipped over CoreDevice in feat: add ios-device screenshot backed by devicectl #118. The contract also needs to express "supported by the tool, refused by this device" — CoreDevice screen recording is capability-gated per device.)
Constraints / facts that bound the design
Physical iOS has no geometry and no coordinate input — coordinate tap / swipe / gesture / screenshot cannot be supported (channel limitation, confirmed on-device). Any first-class routing must degrade these cleanly. (Edit 2026-08-27: screenshot is supported over a second channel — CoreDevice xcrun devicectl device capture screenshot, no dev-signing requirement, shipped as ios-device screenshot in feat: add ios-device screenshot backed by devicectl #118. The audit-channel limitation stands for coordinate input; see the correction comment below.)
Discovery cost: ~~FBDeviceControl attachment discovery pumps the run loop up to ~5 s, so unconditionally adding it to every sim-use devices call would regress the common simulator/Android path — hence "opt-in" above.~~ (Edit 2026-08-27: superseded — ~0.4 s with a device attached, ~1 s no-device after fix: bail fast from ios-device discovery when no device is attached #117's empty-grace bail; see the measurement comments below.)
Non-goals for this issue
No implementation, no chosen design — this is the problem catalogue we align on first. Follow-ups will design (a) the device-kind field + listing parity, and (b) the routing/daemon integration, likely as separate changes.
Problem inventory before we design the CLI shape. This issue only catalogues the current state and the inconsistencies; the actual design is deferred to follow-up discussion.
The theme:
sim-use ios-deviceis currently an appendage, not a first-class target. If we commit to physical iOS support, it should behave like every other target (with some capabilities legitimately restricted), rather than living off to the side.Current state
1.
sim-use deviceslisting coveragesim-use devices?simctl list devicesFBDeviceControl(whatios-device devicesalready uses)adb devicesadb devicessimctlonly knows simulators, sosim-use devicesnever shows a plugged-in iPhone — even thoughsim-use ios-device deviceslists it fine through a different code path. Android, by contrast, shows both emulators and physical devices. So the listing is asymmetric: Android physical is visible, iOS physical is not.2. No device-kind field
Device(thesim-use devicesrow /--jsonschema) carriesplatform ∈ {ios, android}, plus free-formstate,runtime,name,deviceId. There is no field distinguishing simulator / emulator / physical:emulator-5554prefix), not a structured field.The
PLATFORMcolumn answers "ios or android" but not "simulator, emulator, or real device", which is exactly the distinction that determines available capabilities.3. Physical iOS is not routed like other targets
ui,tap,screenshot, …)PlatformRouter)ios-devicenamespaceios-devicecall opens its own DTX sessionsim-use ios <verb>(iOS-only extras)sim-use android <verb>sim-use ios-device <verb>(the whole surface)PlatformRouterhas only.iOSSim/.androidcases; a physical iOS UDID resolves to neither and top-level verbs reject it (pointing atios-device).ios-devicesubcommands are plainAsyncParsableCommands that callDeviceSession.withClientdirectly — they do not implementSimUseExecutableCommand, so they bypass the daemon entirely.This contradicts sim-use's stated design: "one command surface; the UDID shape decides the backend." A physical iOS device is the only target you address through a wholly separate command tree.
Problems to solve (design deferred)
sim-use devicesshould surface physical iOS devices (~~probably opt-in givenFBDeviceControldiscovery cost; see the ~5 s attachment-quiescence wait~~), so discovery is uniform across every target. (Edit 2026-08-27: the opt-in rationale is retracted — measured discovery is ~0.4 s with a device attached, and fix: bail fast from ios-device discovery when no device is attached #117 cut the no-device path to ~1 s, so default-on is plausible; see the measurement comments below.)simulator/emulator/physical(name TBD), orthogonal toplatform. Lets the table and--jsonsay "ios physical" vs "ios simulator" vs "android emulator" explicitly, and lets callers reason about which capabilities apply.PlatformRouter+ top-level verb forwarding + the daemon, sosim-use ui/sim-use tapwork against a plugged-in iPhone (with the restricted capability set), instead of requiringios-device. This is the biggest question and where "first-class citizen vs restricted appendage" is actually decided.screenshot,--json, …) need a consistent "capability not available on this target" contract rather than today's hard namespace split. (Edit 2026-08-27: screenshot moved to the supported column — shipped over CoreDevice in feat: add ios-device screenshot backed by devicectl #118. The contract also needs to express "supported by the tool, refused by this device" — CoreDevice screen recording is capability-gated per device.)Constraints / facts that bound the design
/ screenshotcannot be supported (channel limitation, confirmed on-device). Any first-class routing must degrade these cleanly. (Edit 2026-08-27: screenshot is supported over a second channel — CoreDevicexcrun devicectl device capture screenshot, no dev-signing requirement, shipped asios-device screenshotin feat: add ios-device screenshot backed by devicectl #118. The audit-channel limitation stands for coordinate input; see the correction comment below.)@Ncannot be a cached-coordinate replay (tracked separately in ios-device: preserve the @N ergonomic via fingerprint re-resolution (and a feedback path to harden simulator @N) #113).FBDeviceControlattachment discovery pumps the run loop up to ~5 s, so unconditionally adding it to everysim-use devicescall would regress the common simulator/Android path — hence "opt-in" above.~~ (Edit 2026-08-27: superseded — ~0.4 s with a device attached, ~1 s no-device after fix: bail fast from ios-device discovery when no device is attached #117's empty-grace bail; see the measurement comments below.)Non-goals for this issue
No implementation, no chosen design — this is the problem catalogue we align on first. Follow-ups will design (a) the device-kind field + listing parity, and (b) the routing/daemon integration, likely as separate changes.