Skip to main content

IosDriver

Struct IosDriver 

Source
pub struct IosDriver { /* private fields */ }
Expand description

Driver wrapping HttpRunnerClient with host-side resolve dispatch.

Called IosDriver to make the platform explicit in the cross-platform Driver trait architecture. The type alias pub type SimctlDriver = IosDriver (in lib.rs) keeps existing callers working without source edits.

Implementations§

Source§

impl IosDriver

Source

pub fn new(runner: HttpRunnerClient) -> Self

Source

pub fn runner(&self) -> &HttpRunnerClient

Source

pub fn runner_mut(&mut self) -> &mut HttpRunnerClient

Mutable accessor for the wrapped client. Used by the Driver-trait pass-through (set_target_bundle_id / set_auto_activate) so the client’s per-request context can be mutated after driver construction.

Source

pub fn with_target_bundle_id<S: Into<String>>(self, bundle: S) -> Self

Set the target bundle id sent to the runner on every request. Threads --bundle-id from the CLI down to the wire so the runner’s per-request rebind logic can resolve to the right XCUIApplication.

Source

pub fn with_auto_activate(self, activate: bool) -> Self

Enable App-Activate: true on every request. Forces the runner to call .activate() on the resolved target before each operation. Wired from smix run --activate.

Source

pub async fn tree( &self, include: Option<IncludeScope>, ) -> Result<A11yNode, ExpectationFailure>

Fetch full a11y tree (passthru to GET /tree).

Source

pub async fn describe(&self) -> Result<ScreenDescription, ExpectationFailure>

Aggregate ScreenDescription (DFS visible summaries; no screenshot here — caller adds when needed).

Source

pub async fn find_one( &self, selector: &Selector, include: Option<IncludeScope>, ) -> Result<Option<A11yNode>, ExpectationFailure>

Resolve selector → single node (passthru-ish: driver.tree + resolver).

Source

pub async fn find_norm_coord( &self, selector: &Selector, ) -> Result<Option<(f64, f64)>, ExpectationFailure>

Resolve selector → its centroid as viewport-normalized (nx, ny) in [0, 1]. None when selector resolves no node OR the matched node has an empty / offscreen frame. Used by adapter AnchorRelative dispatch to find an anchor’s center before adding a (dx, dy) shift.

Source

pub async fn find_all( &self, selector: &Selector, include: Option<IncludeScope>, ) -> Result<Vec<A11yNode>, ExpectationFailure>

Resolve selector → all matching nodes.

Source

pub async fn find( &self, selector: &Selector, include: Option<IncludeScope>, ) -> Result<bool, ExpectationFailure>

Boolean existence quick-probe.

Selector-type dispatch:

  • Text selectors with no spatial / index modifiers → runner /find route (Apple element query, no full-tree snapshot — the fast path).
  • Id / Label / Role / Focused / Anchor selectors, and Text with spatial (below / near / …) or index (nth / first / last) modifiers → host-resolve fallback: tree() + resolve_selector_all() client-side. The runner /find route only knows how to query by text predicate, so anything richer has to ride on the full tree snapshot.

Both dispatch branches poll within TOTAL_TIMEOUT_MS on /find 500 or /tree 500 (sim still launching, runner re-attach, etc.), matching the wait_for transient-retry pattern.

Source

pub async fn system_popups( &self, include: Option<IncludeScope>, ) -> Result<Vec<SystemPopup>, ExpectationFailure>

GET /system-popups passthru.

Source

pub async fn system_popup_action( &self, popup_id: &str, button_id: &str, ) -> Result<bool, ExpectationFailure>

POST /system-popup-action passthru. Returns Ok(true) when the runner matched and tapped, Ok(false) on 404 not_found. Transport errors map to ExpectationFailure.

Source

pub async fn tap( &self, selector: &Selector, include: Option<IncludeScope>, ) -> Result<(), ExpectationFailure>

Tap a selector. Host-side resolve → centroid → runner.tap_at_norm_coord (Apple native event chain), with a 5s implicit wait + retry loop.

The resolve+centroid+normalize pipeline lives in the smix_host_coord_resolver stone; this method orchestrates the smix-specific 5s implicit-wait loop plus AI-readable failure rendering + runner injection.

Source

pub async fn tap_with_mode( &self, selector: &Selector, mode: TapMode, include: Option<IncludeScope>, ) -> Result<(), ExpectationFailure>

Tap a selector via an explicit dispatch mode. Used to opt into the runner-side daemonProxySynthesize path for RN Pressable buttons whose RCTTouchHandler gesture recognizer does not fire onPress when the host-side tap_at_norm_coord Apple native event chain is used.

TapMode::DaemonProxySynthesize routes through POST /tap (runner resolves selector + synthesizes via XCTRunnerDaemonSession.daemonProxy ._XCT_synthesizeEvent:completion:). The existing SimctlDriver::tap method keeps the host-resolve + tap_at_norm_coord default for callers that don’t need the daemonProxy alternative.

Source

pub async fn double_tap( &self, selector: &Selector, include: Option<IncludeScope>, ) -> Result<(), ExpectationFailure>

Double-tap a selector via swift sim-side XCUIElement.doubleTap(). 5s implicit-wait + retry on transport (mirrors Self::tap_with_mode). Same as Maestro doubleTapOn.

Source

pub async fn long_press( &self, selector: &Selector, duration: Duration, include: Option<IncludeScope>, ) -> Result<(), ExpectationFailure>

Long-press a selector for duration via swift sim-side XCUIElement.press(forDuration:). 5s implicit-wait + retry on transport. Same as Maestro longPressOn.

Source

pub async fn set_orientation( &self, orientation: Orientation, ) -> Result<(), ExpectationFailure>

Rotate sim via swift XCUIDevice.shared.orientation. Routes to the runner-client set_orientation → POST /set-orientation.

Source

pub async fn fill( &self, selector: &Selector, text: &str, include: Option<IncludeScope>, ) -> Result<(), ExpectationFailure>

Fill text into the focused / matched input.

chunked-fill: the swift runner’s daemon _XCT_sendString path bursts every keystroke at up to 200 chars/sec, which on real-sim outraces React Native’s onChangeText debounce on the main thread — only the leading 2-3 keystrokes commit before later ones are dropped by the JS thread reconciler. The fix is host-side: split text into 1-char chunks and post each to runner /fill separately, with an INTER_CHAR_PAUSE_MS gap so the JS thread can flush its onChangeText callback before the next keystroke fires.

selector-type dispatch (mirrors find()): runner /fill only accepts text selectors (selector.text field or the _focused_ magic). For Id / Label / Role / Anchor / Text-with-modifiers selectors, host-resolve + tap the target first to give it keyboard focus, then fill via the _focused_ magic. Text-without-modifiers selectors take the fast path (direct chunked fill).

Source

pub async fn clear( &self, selector: &Selector, include: Option<IncludeScope>, ) -> Result<(), ExpectationFailure>

Clear the focused / matched input.

Selector-type dispatch (mirrors fill()).

The runner /clear route only accepts text selectors (selector.text field) or the _focused_ magic. For Id / Label / Role / Anchor / Text-with-modifiers selectors, host-resolve + tap the target first so it owns keyboard focus, then clear via the _focused_ magic (single round-trip — clear is not chunked like fill).

Source

pub async fn press_key(&self, key: KeyName) -> Result<(), ExpectationFailure>

POST /press-key passthru.

Source

pub async fn scroll( &self, selector: &Selector, direction: SwipeDirection, ) -> Result<(), ExpectationFailure>

Host-side scroll-until-visible loop. Alternates driver.tree + resolve_selector (host-side probe) with runner.swipe_once (single-swipe runner gesture). Up to 30 swipes or 20s timeout.

Source

pub async fn swipe_once( &self, direction: SwipeDirection, ) -> Result<(), ExpectationFailure>

POST /swipe-once passthru.

Source

pub async fn hide_keyboard(&self) -> Result<(), ExpectationFailure>

POST /hide-keyboard passthru.

Source

pub async fn back(&self) -> Result<(), ExpectationFailure>

POST /back passthru.

Source

pub async fn tap_at_norm_coord( &self, nx: f64, ny: f64, ) -> Result<(), ExpectationFailure>

Tap at normalized (nx, ny) coordinates — escape hatch for coord-based maestro yaml port (§9 #3 lift).

(nx, ny) must be in [0, 1] (normalized to viewport). The runner converts to device pixels via the Apple native event chain (same path as the regular tap() centroid pipeline).

Escape hatch only — selector path (tap(&selector)) is the canonical surface. This bypasses a11y resolve entirely. See CLAUDE.md §9 #3.

Source

pub async fn tap_by_id(&self, id: &str) -> Result<(), ExpectationFailure>

POST /tap-by-id {id} passthru — XCUIElement.tap() via the XCTest gesture-recognizer chain. SwiftUI .sheet / .alert / .confirmationDialog / .fullScreenCover dismiss buttons need this path because the default host-HID-at-coord injects an IOKit-level touch that doesn’t fire the modal’s SwiftUI binding closure. Returns ElementNotFound when the runner reports ok=false.

Source

pub async fn webview_eval(&self, js: &str) -> Result<Value, ExpectationFailure>

Eval JS via the app-side WKWebView bridge. Direct HTTP POST to 127.0.0.1:28080/eval (iOS sim shares host loopback) — does NOT use the XCUITest runner. Returns the parsed JS result as a JSON Value; surfaces bridge error / transport failure as DriverError.

Source

pub async fn find_text_by_ocr( &self, text: &str, locales: &[String], recognition_level: &str, ) -> Result<Option<OcrFrame>, ExpectationFailure>

POST /find-text-by-ocr passthru — Apple Vision OCR over the current XCUIScreen screenshot. Returns the matching text observation’s bounding box (UIKit normalized) or None.

Source

pub async fn swipe_at_norm_coord( &self, from: (f64, f64), to: (f64, f64), ) -> Result<(), ExpectationFailure>

POST /swipe-at-norm-coord {from, to} passthru — escape hatch from-to swipe gesture sibling to Self::tap_at_norm_coord. from / to are normalized to viewport (0, 1). §9 #3 lift companion to tap_at_coord.

Source

pub async fn foreground( &self, bundle_id: &str, ) -> Result<(), ExpectationFailure>

POST /foreground {bundleId} passthru.

Source

pub async fn wait_for( &self, selector: &Selector, timeout: Duration, include: Option<IncludeScope>, ) -> Result<A11yNode, ExpectationFailure>

Poll until the selector resolves to a node, or timeout fires. Returns the matched node (cloned). 5s default budget, 250 ms poll interval.

Source

pub async fn dispose(&self) -> Result<(), ExpectationFailure>

Idempotent close hook. The current implementation has no sidecar / no cache to tear down; reserved for future use.

Trait Implementations§

Source§

impl Driver for IosDriver

Source§

fn set_target_bundle_id(&mut self, bundle: &str)

v0.2.1 — iOS impl: mutate the wrapped HttpRunnerClient so every subsequent request carries the App-Bundle-Id header. Runner side rebinds XCUIApplication(bundleIdentifier:) per request.

Source§

fn set_auto_activate(&mut self, activate: bool)

v0.2.1 — iOS impl: mutate the wrapped client to send App-Activate: true on every request.

Source§

fn set_force_key_events(&mut self, force: bool)

v1.0 Phase A7 — iOS impl: force key-event dispatch mode on the wrapped client. Sends Input-Dispatch-Mode: key-events header on every request; runner-side dispatches via daemon key events instead of a11y-anchored XCUIElement.typeText.

Source§

fn platform(&self) -> Platform

Platform identifier — Platform::Ios or Platform::Android.
Source§

fn as_ios_driver(&self) -> Option<&IosDriver>

iOS-only escape hatch: access IosDriver’s inherent runner() for capsule recording (start_record / stop_record) + describe() aggregation. Android impl returns None. Default None so non-iOS impls don’t need to implement.
Source§

fn tree<'life0, 'async_trait>( &'life0 self, include: Option<IncludeScope>, ) -> Pin<Box<dyn Future<Output = Result<A11yNode, ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Fetch full a11y tree (GET /tree).
Source§

fn find<'life0, 'life1, 'async_trait>( &'life0 self, selector: &'life1 Selector, include: Option<IncludeScope>, ) -> Pin<Box<dyn Future<Output = Result<bool, ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Boolean existence quick-probe.
Source§

fn find_one<'life0, 'life1, 'async_trait>( &'life0 self, selector: &'life1 Selector, include: Option<IncludeScope>, ) -> Pin<Box<dyn Future<Output = Result<Option<A11yNode>, ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Resolve selector → single matching node.
Source§

fn find_all<'life0, 'life1, 'async_trait>( &'life0 self, selector: &'life1 Selector, include: Option<IncludeScope>, ) -> Pin<Box<dyn Future<Output = Result<Vec<A11yNode>, ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Resolve selector → all matching nodes.
Source§

fn find_norm_coord<'life0, 'life1, 'async_trait>( &'life0 self, selector: &'life1 Selector, ) -> Pin<Box<dyn Future<Output = Result<Option<(f64, f64)>, ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Resolve selector → centroid as viewport-normalized (nx, ny).
Source§

fn find_text_by_ocr<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, text: &'life1 str, locales: &'life2 [String], recognition_level: &'life3 str, ) -> Pin<Box<dyn Future<Output = Result<Option<OcrFrame>, ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Apple Vision / ML Kit OCR find text in current screenshot. recognition_level is "accurate" or "fast".
Source§

fn system_popups<'life0, 'async_trait>( &'life0 self, include: Option<IncludeScope>, ) -> Pin<Box<dyn Future<Output = Result<Vec<SystemPopup>, ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

List visible system-level popups (alerts, permission dialogs).
Source§

fn system_popup_action<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, popup_id: &'life1 str, button_id: &'life2 str, ) -> Pin<Box<dyn Future<Output = Result<bool, ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Tap a button on a system popup by id.
Source§

fn wait_for<'life0, 'life1, 'async_trait>( &'life0 self, selector: &'life1 Selector, timeout: Duration, include: Option<IncludeScope>, ) -> Pin<Box<dyn Future<Output = Result<A11yNode, ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Wait until selector resolves (returns matched node when ready).
Source§

fn tap<'life0, 'life1, 'async_trait>( &'life0 self, selector: &'life1 Selector, include: Option<IncludeScope>, ) -> Pin<Box<dyn Future<Output = Result<(), ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Tap an element (default mode = element-anchored coord tap).
Source§

fn tap_with_mode<'life0, 'life1, 'async_trait>( &'life0 self, selector: &'life1 Selector, mode: TapMode, include: Option<IncludeScope>, ) -> Pin<Box<dyn Future<Output = Result<(), ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Tap with explicit mode (Path A vs Path B).
Source§

fn tap_at_norm_coord<'life0, 'async_trait>( &'life0 self, nx: f64, ny: f64, ) -> Pin<Box<dyn Future<Output = Result<(), ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Tap at viewport-normalized coordinate (§9 #3 escape hatch).
Source§

fn tap_by_id<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<(), ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Tap by accessibility identifier via the swift /tap-by-id route (IOHID synthesize at element-frame center).
Source§

fn double_tap<'life0, 'life1, 'async_trait>( &'life0 self, selector: &'life1 Selector, include: Option<IncludeScope>, ) -> Pin<Box<dyn Future<Output = Result<(), ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Double-tap a selector.
Source§

fn long_press<'life0, 'life1, 'async_trait>( &'life0 self, selector: &'life1 Selector, duration: Duration, include: Option<IncludeScope>, ) -> Pin<Box<dyn Future<Output = Result<(), ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Long-press a selector for duration.
Source§

fn fill<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, selector: &'life1 Selector, text: &'life2 str, include: Option<IncludeScope>, ) -> Pin<Box<dyn Future<Output = Result<(), ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Fill text into a focused / matched input.
Source§

fn clear<'life0, 'life1, 'async_trait>( &'life0 self, selector: &'life1 Selector, include: Option<IncludeScope>, ) -> Pin<Box<dyn Future<Output = Result<(), ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Clear text from a focused / matched input.
Source§

fn press_key<'life0, 'async_trait>( &'life0 self, key: KeyName, ) -> Pin<Box<dyn Future<Output = Result<(), ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Press a named key (Return / Tab / Backspace / etc).
Source§

fn scroll<'life0, 'life1, 'async_trait>( &'life0 self, selector: &'life1 Selector, direction: SwipeDirection, ) -> Pin<Box<dyn Future<Output = Result<(), ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Scroll until a selector is visible.
Source§

fn swipe_once<'life0, 'async_trait>( &'life0 self, direction: SwipeDirection, ) -> Pin<Box<dyn Future<Output = Result<(), ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

One-shot swipe in a direction (no probe loop).
Source§

fn swipe_at_norm_coord<'life0, 'async_trait>( &'life0 self, from: (f64, f64), to: (f64, f64), ) -> Pin<Box<dyn Future<Output = Result<(), ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Swipe between two viewport-normalized coords.
Source§

fn hide_keyboard<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<(), ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Dismiss the keyboard.
Source§

fn back<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<(), ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Trigger “back” gesture (iOS edge-swipe / Android KEYCODE_BACK).
Source§

fn set_orientation<'life0, 'async_trait>( &'life0 self, orientation: Orientation, ) -> Pin<Box<dyn Future<Output = Result<(), ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Rotate the device.
Source§

fn foreground<'life0, 'life1, 'async_trait>( &'life0 self, bundle_id: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<(), ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Bring app to foreground.
Source§

fn webview_eval<'life0, 'life1, 'async_trait>( &'life0 self, js: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<Value, ExpectationFailure>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Evaluate JavaScript in a WebView (iOS: fixture bridge :28080; Android: Chrome DevTools Protocol via adb forward).

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more