Module: Everywhere::Simulator
- Defined in:
- lib/everywhere/simulator.rb
Overview
Thin wrappers around xcrun simctl — just enough to boot a Simulator,
install the stamped shell and launch it. Anything fancier (choosing
devices, multiple simulators) belongs in the user's own Xcode workflow.
Class Method Summary collapse
-
.boot_default! ⇒ Object
Boot the newest available iPhone (or reuse a booted one) and bring the Simulator app to the foreground.
-
.booted_udid ⇒ Object
UDID of a booted iPhone Simulator, or nil.
-
.devices ⇒ Object
Available devices of the current runtime set, oldest runtime first —
.last/reverse.findtherefore prefer the newest. - .install(udid, app_path) ⇒ Object
-
.launch(udid, bundle_id, env: {}) ⇒ Object
Launch (relaunching if already running).
- .wait_until_booted(udid, timeout: 60) ⇒ Object
Class Method Details
.boot_default! ⇒ Object
Boot the newest available iPhone (or reuse a booted one) and bring the Simulator app to the foreground. Returns the UDID.
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/everywhere/simulator.rb', line 21 def boot_default! if (udid = booted_udid) Shellout.run?("open", "-a", "Simulator", quiet: true) return udid end device = devices.reverse.find { |d| d["name"].to_s.match?(/\AiPhone/) } || devices.last UI.die!("no iOS Simulator devices — open Xcode once to install a Simulator runtime") unless device UI.step("booting simulator #{UI.bold(device["name"])}") Shellout.run?("xcrun", "simctl", "boot", device["udid"], quiet: true) # races `open` starting it; harmless Shellout.run?("open", "-a", "Simulator", quiet: true) wait_until_booted(device["udid"]) device["udid"] end |
.booted_udid ⇒ Object
UDID of a booted iPhone Simulator, or nil.
15 16 17 |
# File 'lib/everywhere/simulator.rb', line 15 def booted_udid devices.find { |d| d["state"] == "Booted" }&.fetch("udid", nil) end |
.devices ⇒ Object
Available devices of the current runtime set, oldest runtime first —
.last / reverse.find therefore prefer the newest.
55 56 57 58 59 60 61 62 |
# File 'lib/everywhere/simulator.rb', line 55 def devices out, status = Shellout.capture("xcrun", "simctl", "list", "devices", "available", "-j") return [] unless status&.success? JSON.parse(out).fetch("devices", {}).sort.flat_map { |_runtime, list| list } rescue JSON::ParserError [] end |
.install(udid, app_path) ⇒ Object
37 38 39 40 |
# File 'lib/everywhere/simulator.rb', line 37 def install(udid, app_path) out, status = Shellout.capture("xcrun", "simctl", "install", udid, app_path) UI.die!("simctl install failed: #{out.strip}") unless status&.success? end |
.launch(udid, bundle_id, env: {}) ⇒ Object
Launch (relaunching if already running). Env vars are forwarded into the app's process via simctl's SIMCTL_CHILD_ prefix — how the dev URL reaches the shell without restamping the bundle.
45 46 47 48 49 50 51 |
# File 'lib/everywhere/simulator.rb', line 45 def launch(udid, bundle_id, env: {}) child_env = env.transform_keys { |k| "SIMCTL_CHILD_#{k}" } out, status = Shellout.capture(child_env, "xcrun", "simctl", "launch", "--terminate-running-process", udid, bundle_id) UI.die!("simctl launch failed: #{out.strip}") unless status&.success? end |
.wait_until_booted(udid, timeout: 60) ⇒ Object
64 65 66 67 68 69 70 71 72 |
# File 'lib/everywhere/simulator.rb', line 64 def wait_until_booted(udid, timeout: 60) deadline = Time.now + timeout until Time.now > deadline return if devices.any? { |d| d["udid"] == udid && d["state"] == "Booted" } sleep 1 end UI.die!("simulator never finished booting") end |