simgadget
    Preparing search index...

    Class Simulator

    Index
    udid: string
    name: string
    deviceType?: DeviceTypeInfo

    The device type this simulator was created as — {identifier, name}, where name is the model a person says ("iPhone 16 Pro") and identifier is what simctl create takes.

    undefined on an attached handle, deliberately. createSimulator resolves the device type on its way to simctl create and so knows it for free; attachSimulator adopts a udid and never looks one up. A caller that needs it for an attached simulator can pay a listSimulators() for the identifier — which is why this is a field a creator fills rather than a lookup the handle performs.

    Exists because the answer to start_simulator names the model: an agent asks for "iPhone" and the reply is the only place it learns which iPhone it got.

    • get lastBoot(): ReadyResult | undefined

      How the last boot/waitReady went; set by createSimulator (through boot()), boot() and waitReady(). Undefined on a fresh attach. A getter rather than a readonly field because the boot ladder finishes long after the constructor has run, and a readonly field cannot be assigned outside the constructor that declares it.

      Returns ReadyResult | undefined

    • Boots and waits until driveable — the whole sequence BOOT_BUG.md taught: simctl boot, open -a Simulator.app (DECISIONS.md #1), then waitUntilDriveable's bootstatus wait, settle and poll. This method owns the whole sequence; createSimulatorWith calls it rather than repeating the steps and writing the result back into the handle afterwards.

      Does not throw on timeout — the simulator exists either way, and a throw would discard the handle and the udid with it; inspect the result. No-op boot (already booted) still performs the wait: simctl boot fails "Unable to boot device in current state: Booted" for one, and that one shape is swallowed so the wait still runs. Any other simctl boot failure — a genuinely bad udid, a corrupted device — still propagates.

      Parameters

      • Optionalopts: { budgetMs?: number }

      Returns Promise<ReadyResult>

    • Brings the Simulator.app window to the front for this simulator, without booting or waiting for anything.

      Separate from boot() because the MCP's resume path wants exactly this and nothing else: a session reconnecting to a simulator that is already up needs its window visible again, and boot() — the only other place that opens the app (DECISIONS.md #1) — would charge it the whole driveability ladder, whose settle is unconditional. Sub-second here, eight seconds and change there, on the call an agent makes most often after a disconnect.

      open -a raises whatever Simulator.app is showing rather than choosing a device, which is why this takes no argument and promises no more than it does: with several simulators booted, the frontmost window is not necessarily this one. Booting is what makes a device frontmost, and this method is deliberately not that.

      Returns Promise<void>

    • Waits (without booting) until an accessibility read answers with a real frame. This is what the MCP's attach_simulator does after adopting. Same ladder as boot()'s wait, so "costs nothing when already up" is as true here as it is there: simctl bootstatus on an already-booted device returns immediately.

      Parameters

      • Optionalopts: { budgetMs?: number }

      Returns Promise<ReadyResult>

    • Tolerates an already-shut-down simulator, as today's cleanup does (index.ts:481): a blanket swallow, not just the one shape boot() matches, because there is nothing else useful to report from a shutdown that "failed" only because there was nothing left to shut down.

      Returns Promise<void>

    • Shuts down and deletes the simulator. Ports destroy_simulator's sequence (index.ts:1337-1351):

      1. closeCompanion(udid)before any simctl call. simctl shutdown/delete takes seconds, and without the block a concurrent call for this simulator would see its companion channel die and spawn a replacement against a simulator that is about to stop existing (see CompanionManager.close's header).
      2. simctl shutdown, tolerating "already shut down" the same way shutdown() does.
      3. simctl delete — not swallowed; a real failure here means the simulator was not actually deleted, so the handle must not be marked stale for it.
      4. Mark the handle stale and forget this udid's recovery state (today's forgetSimulator, index.ts:888).

      Before any of that, a recording this handle started is stopped. Deleting the device out from under simctl io ... recordVideo does not stop it: observed leaving a recorder running six minutes later against a udid that no longer existed. Stopping first is also the only ordering that can finalize the file, since after the delete there is nothing left to record.

      Returns Promise<void>

    • .app directory or .ipa. Throws app-bundle-not-found before calling simctl if the path does not exist — simctl does not support -- as an option terminator, and this existsSync check is what makes passing a caller-supplied path through safe.

      Parameters

      • appPath: string

      Returns Promise<void>

    • Parameters

      • bundleId: string
      • Optionalopts: { terminateRunning?: boolean }

      Returns Promise<{ pid: number | null }>

    • The screen as a caller should see it. Absorbs today's ui_describe_all tool body (index.ts:1548-1560): when the pruned read comes back degenerate, run describeAll's whole ladder of cures — restart our companion, then the simulator's bridge — and ask again if it brings the screen back. Returning the screen beats returning an error that tells the caller to retry the call themselves.

      Returns Promise<ScreenRead>

    • Logical screen dimensions from the cheap (~13ms) unpruned read — today's getScreenDimensions (index.ts:603) without the caching, which is now noteRootFrame's business. This replaces public access to the raw tree, which no caller of the old code ever consumed for anything else (SIMGADGET.md's Decisions register).

      Returns Promise<{ width: number; height: number }>

    • Resolves an element by its accessibility identifier — exact, where a label is a substring that can drift onto something else as a screen changes. Ports findByIdentifier (index.ts:180).

      No AXBridge fallback: this exists to re-read an element the caller has already found, so a miss means it has genuinely gone rather than that a backend cannot see it, and paying ~300ms to confirm a disappearance helps nobody.

      Parameters

      • identifier: string

      Returns Promise<AXElement | null>

    • Resolves a single element by the text a caller knows it by. Ports findByLabel (index.ts:232).

      Cheap path first: the companion matches a marker server-side and returns just that element, roughly half a kilobyte against several for a whole tree, in ~13ms. Most lookups end there.

      When it misses, the fallback reads the screen and matches here. That covers three separate failures the marker query cannot:

      • Apple's translator omits whole containers, so a control in a tab bar, nav bar or toolbar is absent from the tree the marker query searches even though it carries the label and hit-tests fine. The AXBridge read sees the app's real view hierarchy instead.
      • The match is on AXLabel only, but a control's visible text is not always its label — search fields in particular have a null label and their text in AXValue, making them unnameable.
      • The match is exact, so a caller's ASCII apostrophe never finds iOS's typographic one.

      One fallback rather than a chain of marker retries: it is a single round trip (~350ms against ~300ms for another marker query), and matching here means the comparison is ours to fix rather than the companion's to be exact about.

      Parameters

      • label: string

      Returns Promise<AXElement | null>

    • The element at a logical-space point, or null when nothing is there. Absorbs the ui_describe_point tool body (index.ts:2110-2152).

      A frame nowhere near the point it was found at is the telltale sign of a remote-hosted view: the hit-test is right, the frame is measured from the hosting window rather than the screen. The tree read that corrects it costs ~300ms, so it is paid only here, on the reads that are otherwise wrong — and only to replace the rectangle, never the identity, which the point read established by hit-testing.

      Parameters

      • x: number
      • y: number

      Returns Promise<AXElement | null>

    • Rotates the device, waits out the animation, then detects what the interface adopted and reports both. Ports the rotate tool body (index.ts:1454).

      Detected, not assumed. An app is free to decline an orientation — and one always does: no Face ID iPhone will adopt upside-down portrait, whatever its Info.plist says. Reporting the request back as though it had been obeyed would leave every later coordinate wrong, silently, which is exactly the failure the returned adopted exists to make visible.

      Parameters

      Returns Promise<RotateResult>

    • Probes the current orientation and refreshes this handle's hint. Ports the detect_rotation tool body (index.ts:1516).

      The cached logical dimensions go first: a rotation swaps them, and a probe that read the stale pair would compute every candidate position in the space the screen just left.

      Returns Promise<Orientation>

    • Tap by label or by coordinate. Two verbs under one name, because callers think of them as one:

      • {x, y} is a literal touch at the caller's coordinates: no resolution, no verification, delivered with the 0.1s floor. Coordinates are the caller saying where, and are taken at their word. Answers acted: "touch" with no element.
      • {label} is "find this thing and operate it", and the order below is the specification: resolve, refuse a disabled control, activate a toggle through accessibility (falling back to a real touch when the action API cannot reach it), refuse a hold or multi-tap aimed at a toggle, take the centre of the frame, transform it, hit-test it, and only then touch.

      Every branch of that order exists because a tap once silently did the wrong thing and reported success; the reasons are on the pieces (./ax/tap.ts's decideTapVerb, activateToggle and the hit-test below).

      Parameters

      Returns Promise<TapResult>

    • Types printable ASCII and newline as key events.

      The refusal is here, at the library boundary, rather than left to the idb client's own — which raises an IdbError naming keycodes. Checking twice costs a set membership per character and buys the thing design rule 2 asks for: the typed error is what escapes, with the distinct offending characters on it, and no caller has to read a message to find out what they were. Before any event goes out, either way: half a string typed into an app is not a failure a caller can undo.

      Exclusive, so another caller's taps cannot land mid-string.

      Parameters

      • text: string

      Returns Promise<void>

    • A swipe between two points in logical space.

      Void because there is genuinely nothing to read back: the companion acks delivery and knows no more than we do about what the app made of it.

      Both endpoints go through one resolved transform, so they cannot end up in different coordinate spaces (see portraitTransform). delta and durationSeconds pass through as given — the client already substitutes 0 for undefined, and the defaults a caller sees belong to whatever host is asking (DECISIONS.md #15).

      Exclusive: a swipe is a stream of events, and another caller's input landing between them scrambles the gesture.

      Parameters

      • from: { x: number; y: number }
      • to: { x: number; y: number }
      • Optionalopts: { durationSeconds?: number; delta?: number }

      Returns Promise<void>

    • A hardware button. HOME is the only way to leave an app without launching another, which is the first thing a short script needs.

      Parameters

      • button: "home" | "lock" | "side-button" | "siri" | "apple-pay"
      • Optionalopts: { durationSeconds?: number }

      Returns Promise<void>

    • A screenshot of the screen, rotated to match the interface.

      simctl captures in physical portrait pixel orientation whatever the device is doing, so a landscape capture arrives on its side; every caller of this library would then have to know that and undo it. Rotating here is a deliberate change from today's screenshot tool, which saves the raw capture — the spec's Screenshot type mandates it, and orientation says which way up the returned image is.

      resizeTo: "points" is the option ui_view was built out of: the image comes back in the coordinate space the caller's own taps live in, which is both far smaller than native pixels and directly comparable with anything a describe reported.

      Parameters

      Returns Promise<Screenshot>

    • Starts recording video to path, and resolves once it is under way.

      One recording per handle: a second call throws rather than silently replacing the first, because the process it would abandon holds the only reference to a file that never gets finalized.

      Parameters

      Returns Promise<void>

    • Stops the recording and returns where it was written.

      The path comes from the handle rather than from the caller: startRecording resolved it, and answering with anything else would hand back a relative path the caller would have to resolve the same way to use.

      Returns Promise<{ path: string }>

    • Restarts the guest's CoreSimulator bridge (the wedge cure). Step 3's recovery machinery calls restartSimulatorBridge (./lifecycle.ts) itself, best-effort; this is the same command, public for a host that wants to force it, with a real failure surfaced rather than swallowed.

      Returns Promise<void>

    • Stops this simulator's companion process. The exit hook (CompanionManager's process.on("exit")) does this anyway on process exit; long-lived hosts get tidier teardown by calling it themselves. The simulator itself keeps running, state intact.

      Returns Promise<void>