Skip to content

feat!: replace node-ios-device with devicectl (CoreDevice) - #261

Merged
cb1kenobi merged 4 commits into
mainfrom
feature/devicectl
Aug 9, 2026
Merged

cb1kenobi merged 4 commits into
mainfrom
feature/devicectl

Conversation

@hansemannn

@hansemannn hansemannn commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Warning

BREAKING CHANGE — released as 6.0.0. Requires Xcode 15+, changes the device info shape and removes the install() log relay (use launch() instead). Details under Breaking changes below.

Summary

Reimplements the device module on top of xcrun devicectl (CoreDevice, Xcode 15+) and removes the native node-ios-device module — along with its Node-ABI-specific prebuilt binaries. iOS 17+ devices connect exclusively through CoreDevice/RemoteXPC and are invisible to the old MobileDevice.framework API, even over USB.

Ported APIs

node-ios-device 1.13.0 Replacement
devices() device.detect() — via devicectl list devices; now also detects Wi-Fi / local-network devices
installApp() device.install() — via devicectl device install app; also works over Wi-Fi
log(udid, port) device.launch() — launches the app with an attached console and relays log lines (app-started / log / app-quit events); waits for a locked device to be unlocked first
trackDevices() not ported — was never exposed by ioslib and has no consumers

New: device.lockState(udid, cb) exposes the device lock state.

Breaking changes

  • Requires Xcode 15+ command line tools
  • Device info shape: deviceColor, hardwareModel, modelNumber, serialNumber removed; identifier, marketingName, connectionType added
  • device.install() no longer relays app logs via options.logPort — use device.launch() instead
  • device.detect() failures now degrade to an empty device list plus a warning issue instead of failing the entire ioslib.detect() call

Testing

Verified against a physical iPhone (iOS 27) over Wi-Fi: detection, lock state, install (~6s), launch with full console capture (stdout + stderr/NSLog incl. multi-line messages), locked-device wait → unlock → launch, and clean app-quit propagation. The bundled test/TestApp fixture was modernized for this flow (no stderr file redirect, self-quits after 10s).

Device detection, install and launch now go through `xcrun devicectl`
(Xcode 15+). The native node-ios-device module is removed: iOS 17+
devices connect exclusively through CoreDevice/RemoteXPC and are
invisible to the old MobileDevice.framework API, even over USB.

- device.detect() shells out to `devicectl list devices` and returns
  paired physical iOS devices (incl. Wi-Fi/local network devices);
  failures degrade to an empty device list with a warning issue instead
  of failing the whole ioslib.detect() call
- device.install() installs via `devicectl device install app`; the
  logPort TCP relay is gone
- new device.launch() launches with an attached console and emits log
  lines, waiting for a locked device to be unlocked first
- new device.lockState() exposes the device lock state

BREAKING CHANGE: requires Xcode 15+ command line tools. The device info
shape changed (deviceColor, hardwareModel, modelNumber and serialNumber
are gone; identifier, marketingName and connectionType are new) and
device.install() no longer relays app logs via options.logPort - use
device.launch() instead.
Verified against a physical iPhone over wifi:

- buffer the app's stderr until stdout activity confirms the launch, so
  a failed launch's devicectl error block never leaks into the app log
  stream (devicectl routes the app's stderr - i.e. all NSLog output -
  to its own stderr, together with its error blocks)
- filter devicectl's own progress lines from the log stream and emit
  the restored app-started event when the launch is confirmed
- recognize SpringBoard's 'Locked' denial in the devicectl output and
  return to waiting instead of erroring; lockState can time out on
  locked devices over wifi
- only a definitive 'unlocked' lockState answer triggers a launch
  attempt while waiting, an unknown state keeps polling
- TestApp: stop redirecting stderr to a file (the console capture needs
  it), log to both stdout and stderr, and exit after 10s so the full
  launch/log/quit cycle is testable
Comment thread package.json
Comment thread lib/device.js Outdated
Uses --json-output - as suggested in the PR review; the dash makes
devicectl stream the JSON directly, while /dev/stdout would break
subcommands like 'device info' that save their output atomically.
@hansemannn
hansemannn requested a review from cb1kenobi August 9, 2026 06:56

@cb1kenobi cb1kenobi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested and works! Nice job!

@cb1kenobi
cb1kenobi merged commit 3e99927 into main Aug 9, 2026
5 checks passed
@cb1kenobi
cb1kenobi deleted the feature/devicectl branch August 9, 2026 22:05
@hansemannn

Copy link
Copy Markdown
Contributor Author

Appreciate it a lot, Chris! Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants