Environment
- rock / @rock-js/platform-apple-helpers: 0.12.8 (same code on
main, 0.15.1)
- macOS 27, Xcode 27
- Reproduced with
rock run:ios after selecting a simulator (both fresh boot and already-booted)
Description
launchSimulator in packages/platform-apple-helpers/src/lib/commands/run/runOnSimulator.ts hardcodes
await spawn('open', [
`${activeDeveloperDir}/Applications/Simulator.app`,
'--args', '-CurrentDeviceUDID', device.udid,
]);
Xcode 27 no longer ships Simulator.app. It is replaced by Device Hub (DeviceHub.app, bundle id com.apple.dt.Devices), which also moves from <Xcode>/Contents/Developer/Applications/ to <Xcode>/Contents/Applications/. open fails on the missing path, spawn rejects, and because launchSimulator runs in a Promise.all with the build, the whole run:ios aborts:
The file /Applications/Xcode.app/Contents/Developer/Applications/Simulator.app does not exist.
Device listing, install and launch (simctl list / simctl install / simctl launch) are unaffected. This is the only place in Rock that references Simulator.app.
Prior art
Rock's platform-apple-helpers is a port of cli-platform-apple, so the same fix applies.
Related: -CurrentDeviceUDID alert on an already booted simulator (same lines)
react-native-community/cli#2820 describes a second problem in the same code, which Rock also has on Xcode ≤ 26: open Simulator.app --args -CurrentDeviceUDID <udid> is passed unconditionally, before the device.state !== 'Booted' guard. For a device that is already booted, Simulator.app re-attempts the boot and shows a modal alert "Unable to boot device in current state: Booted" on every run:ios until the simulator is shut down. The run still succeeds, but the alert has to be dismissed by hand. The Device Hub path is not affected because it never passes -CurrentDeviceUDID.
Proposed fix
- If
<developerDir>/Applications/Simulator.app exists: open it, passing -CurrentDeviceUDID only when the device is not already booted.
- Else if
<developerDir>/../Applications/DeviceHub.app exists: open "devices://device/open?id=<udid>", falling back to opening DeviceHub.app directly if the URL scheme is rejected.
- Else: warn and continue. The app can still be installed and launched headless via
simctl, so a missing GUI should not abort the run.
Currently working around this with a local patch of @rock-js/platform-apple-helpers; PR to follow.
Environment
main, 0.15.1)rock run:iosafter selecting a simulator (both fresh boot and already-booted)Description
launchSimulatorinpackages/platform-apple-helpers/src/lib/commands/run/runOnSimulator.tshardcodesXcode 27 no longer ships
Simulator.app. It is replaced by Device Hub (DeviceHub.app, bundle idcom.apple.dt.Devices), which also moves from<Xcode>/Contents/Developer/Applications/to<Xcode>/Contents/Applications/.openfails on the missing path,spawnrejects, and becauselaunchSimulatorruns in aPromise.allwith the build, the wholerun:iosaborts:Device listing, install and launch (
simctl list/simctl install/simctl launch) are unaffected. This is the only place in Rock that referencesSimulator.app.Prior art
Simulator.appwhen it exists, otherwise opens Device Hub via its URL schemeopen "devices://device/open?id=<UDID>".DeviceHubasSimulatorreplacement for Xcode 27+ expo/expo#46757 (merged 2026-06-10): same detection order (Simulator first, Device Hub second), plus Device Hub in the "is the simulator app running" checks.Rock's
platform-apple-helpersis a port ofcli-platform-apple, so the same fix applies.Related:
-CurrentDeviceUDIDalert on an already booted simulator (same lines)react-native-community/cli#2820 describes a second problem in the same code, which Rock also has on Xcode ≤ 26:
open Simulator.app --args -CurrentDeviceUDID <udid>is passed unconditionally, before thedevice.state !== 'Booted'guard. For a device that is already booted, Simulator.app re-attempts the boot and shows a modal alert "Unable to boot device in current state: Booted" on everyrun:iosuntil the simulator is shut down. The run still succeeds, but the alert has to be dismissed by hand. The Device Hub path is not affected because it never passes-CurrentDeviceUDID.Proposed fix
<developerDir>/Applications/Simulator.appexists: open it, passing-CurrentDeviceUDIDonly when the device is not already booted.<developerDir>/../Applications/DeviceHub.appexists:open "devices://device/open?id=<udid>", falling back to openingDeviceHub.appdirectly if the URL scheme is rejected.simctl, so a missing GUI should not abort the run.Currently working around this with a local patch of
@rock-js/platform-apple-helpers; PR to follow.