AI-native iOS Simulator automation library and CLI, written in Rust.
smix drives iOS-Simulator apps through a typed Rust API designed for
LLM authoring: explicit selectors, AI-readable failure messages with
visible-element context and edit-distance suggestions, host-side resolve
with Apple-native event injection, and an MCP server entry for direct
Claude Code integration.
The project pivoted from TypeScript to Rust in v3 (May 2026); the
original TS implementation is preserved as a port reference under
legacy/. The canonical implementation lives in
crates/ as a cargo workspace following the mailrs
stone-vs-cement layout convention.
# Probe environment (Xcode command-line tools + Simulator state).
cargo run --release -p smix-cli -- doctor
# List simulators (or use `--json` for machine-readable output).
cargo run --release -p smix-cli -- sim list// In your own Rust test or binary:
use smix_sdk::{App, text, KeyName};
use std::time::Duration;
# async fn demo() -> Result<(), smix_sdk::ExpectationFailure> {
let app = App::connect_to_runner(22087).await?
.with_udid("4F0B35D2-03F0-4A8F-B729-09072153E8AE");
app.launch("com.example.app").await?;
app.wait_for(&text("Login"), Duration::from_secs(5)).await?;
app.tap(&text("Login")).await?;
app.fill(&text("Email"), "user@example.com").await?;
app.press_key(KeyName::Return).await?;
app.assert_visible(&text("Dashboard")).await?;
# Ok(())
# }The smix CLI binary and the smix-mcp MCP server are built from this
workspace. The SmixRunner (Swift sub-project at
swift-bridge/) provides the sim-side XCUITest runner
the Rust client talks to over HTTP.
Following mailrs's stone-vs-cement convention:
- Stones are zero-project-coupling, RFC/protocol-bounded crates — a non-smix Rust project could adopt them.
- Cement is smix-specific business code (lifecycle conventions, wire contracts with our Swift runner, CLI / MCP entry binaries).
| Crate | Role |
|---|---|
smix-core |
Barrel + shared placeholder |
smix-screen |
A11yNode / Rect / Role / ElementSummary + visibility primitives |
smix-selector |
Structured Selector enum + match_text + describe_selector |
smix-selector-resolver |
DFS + spatial + visibility resolver (with compile cache) |
smix-input |
KeyName + SwipeDirection enums |
smix-error |
AI-readable ExpectationFailure + build_suggestions + edit-distance |
smix-recorder-ir |
IRAction enum + JSON serialization |
smix-simctl ⭐ |
Generic xcrun simctl child_process wrapper |
| Crate | Role |
|---|---|
smix-runner-client |
HTTP IPC client to swift-bridge/SmixRunnerCore |
smix-driver |
Decide layer (5s implicit wait, scroll loop, dispatch conventions) |
smix-sdk |
Public App surface + ergonomic selector helpers + matchers |
smix-cli |
smix binary (clap-based CLI) |
smix-mcp |
smix-mcp binary (rmcp 1.7 stdio MCP server) |
Following the mailrs convention, every hot-path primitive carries a
tests/perf_gate.rs hard ceiling and a benches/<name>.rs criterion
bench. All numbers come from criterion bench medians on M-series macOS
(see PERFORMANCE.md for the methodology and the
TS baseline comparison table).
Sample v3.1 measurements (Rust release vs TS V8 baseline):
| Operation | TS (ns) | Rust (ns) | Speedup |
|---|---|---|---|
is_visible_enough (in-view) |
21.2 | 0.996 | 21× |
is_visible_enough (zero-bounds reject) |
20.5 | 0.449 | 46× |
match_text_compiled (string label hit) |
36.8 | 4.89 | 7.5× |
match_text_compiled (string miss, 6-field scan) |
41.4 | 3.53 | 12× |
resolve_selector (id base, 100-node tree miss) |
3,234 | 198 | 16× |
dfs_collect (100-node tree composite) |
788 | 233 | 3.4× |
See PERFORMANCE.md for the full table across 25+ operations.
The insight_parity
example exercises 10 real-world iOS flows against an installed app —
the gate v1.9 closed at 5-round strict 10/10, used as the Rust-rewrite
business-level re-verification target for v3.4.
# Once you have: a booted sim, the insight RN app installed, Metro
# bundler running, and the SmixRunnerCore swift binary launched on
# the sim:
export SMIX_UDID="$(xcrun simctl list devices booted | awk '/Booted/{print $NF}' | tr -d '()' | head -1)"
./scripts/insight-parity.shThe script does env / runner / Metro probes, runs the example, and on
failure captures a sim screenshot to /tmp/insight-fail-<ts>.png for
follow-up dig.
- ARCHITECTURE.md — stone-vs-cement model, sense / decide / act three-layer architecture, the dependency policy
- PERFORMANCE.md — perf budget framework + TS baseline reference table
docs/v3.md— current v3 cycle scope and decision logdocs/plan-cold/— minor-version cold backlogsdocs/plan-history/— archived hot plans per checkpoint
smix ships with hard architectural rules; the full list lives in CLAUDE.md. Highlights:
- iOS Simulator only. Real-device automation is out of scope.
- No xpath / no raw-coordinate selector surface. Selectors are structured (text / id / label / role+name / focused / anchor-only).
- No bare
sleepAPI. All wait surfaces are explicit and bounded. - AI-readable failure messages.
ExpectationFailure::to_promptemits structured output a coding agent can act on. - Runner port is localhost-only. No
0.0.0.0bind (compliance gate set in v1.2).
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.