Built for speed, and measured
A tap costs ~1.2 ms of wire time instead of the ~165 ms
it takes to spawn a Python process for every single call. Finding a control by name is
~13 ms on the fast path. A whole screen is ~350 ms.
Asking what is at a point is ~10 ms.
The whole design is shaped by that: tap({label: "Sign Up"}) resolves the
element on the simulator and operates it, so nothing ever pulls down a
kilobyte-scale tree just to find one button.
Almost zero dependencies
The library has exactly two npm runtime dependencies:
@grpc/grpc-js and @bufbuild/protobuf. That's it.
No test framework leaking into your install, no CLI wrappers, no 14 MB of
transitive weight.
That is also why there are two packages. The MCP SDK and Zod are 14 MB
between them, and a library that taps a simulator has no business putting them in
front of you.
A companion from this decade
SimGadget pins a build from current idb source against
Xcode 26.6 / Swift 6.3.3, sha256-verified, downloaded once and cached.
This is not housekeeping — it is what makes the rest possible. Tap-by-name doesn't work
on the old companion. Neither does reading the contents of a tab bar, a nav bar or a
toolbar.
There is deliberately no fallback to whatever idb_companion is on
your $PATH: an old companion doesn't reject request fields it
doesn't understand, it ignores them and answers anyway. A fallback would return results
that are wrong but entirely plausible.
Bugs nothing else has fixed
A long list of failures that cost real debugging time to find, each of them the kind
that reports success while doing nothing. Instant taps that don't register. Switches
that were never tappable. System sheets landing taps 476 points away. Simulators that
wedge on boot.
Every one is measured, and every measurement traces to a run in the repository.