CLI tool
facebook/idb avatar
facebook/idb

idb: Facebook's command line bridge for driving iOS simulators and devices

idb is a flexible command line interface for automating iOS simulators and devices

5,354 stars508 forksSwiftMIT

At a glance

What is it?
idb turns the parts of Xcode that only exist in its GUI into CLI commands, split across a macOS companion and a Python client so that test shards can fan out across a rack of simulators. The catch is that much of it runs on private frameworks.
Who is it for?
idb earns its place when you need UI-level iOS automation at a scale Xcode cannot reach alone, since the companion and client split lets a test job on a Linux box drive simulators inside a macOS lab, and it is also the honest answer to the question of which Xcode actions have no scriptable interface.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Swift, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 7, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Three principles behind the command set

The README opens with the reasoning rather than a feature list, and the three principles are worth reading closely because they explain most of the design. Remote automation comes first: `idb` is composed of a companion that runs on macOS and a Python client that can run anywhere. That split is the whole reason a device lab in a data center is possible, because the machine running the test orchestration does not need to be a Mac.

The second principle is simple primitives. Rather than exposing one enormous automation verb, `idb` publishes granular commands that you sequence yourself, and it states the consistency goal plainly: the primitives aim to behave the same across iOS versions and across simulators and physical devices. For a human at a terminal and for a CI job calling the same subcommands, that uniformity is the feature.

The third principle is exposing missing functionality, and this is the honest caveat. Xcode has a set of actions that exist only inside its user interface, and `idb` reaches them by using the same private frameworks Xcode itself uses. That is where the unusual capabilities come from, and it is also the reason `idb` breaks when Apple changes internals. Read that sentence as both the feature and the limitation.

One brew formula installs both the companion and the client

Installation is split into the two halves of the architecture. The companion is the per-target process that actually talks to a simulator or device, and the README gives it its own heading in the quick start:

bash
brew install facebook/fb/idb

The Python client is installed by that same formula, and can also come from PyPI on its own, which matters when the automation side is a Linux runner:

bash
pip3 install fb-idb

The version floor is specific: the client requires python 3.10 or greater. Details and a guided tour live at fbidb.io, which is where the README sends you once the two commands are in place.

The pairing means there is a per-target handshake to be aware of. A fresh simulator shows up with no companion attached until one is running, which is why the example output below lists a `No Companion Connected` state for every target. Reading that column is the quickest way to tell an installation problem apart from a discovery problem.

Listing targets, listing apps, launching one

The first command after installation enumerates every simulator on the machine, and the README's sample output shows the shape of a row: device name, UDID, current state, whether it is a simulator, the iOS version, the architecture, and companion status.

bash
$ idb list-targets
iPhone 16 | 569C0F94-5D53-40D2-AF8F-F4AA5BAA7D5E | Shutdown | simulator | iOS 26.0 | arm64 | No Companion Connected
iPhone 17 | 2A1C6A5A-0C67-46FD-B3F5-3CB42FFB38B5 | Shutdown | simulator | iOS 26.0 | arm64 | No Companion Connected

From there, `list-apps` takes a UDID and reports what is installed, including bundle identifier, display name, whether the app is a system app, architecture, whether it is running, and whether it is debuggable:

bash
$ idb list-apps --udid 74064851-4B98-473A-8110-225202BB86F6
com.apple.Maps | Maps | system | arm64 | Not running | Not Debuggable
com.apple.MobileSMS | MobileSMS | system | arm64 | Not running | Not Debuggable

Launching is a bundle identifier and nothing else:

bash
$ idb launch com.apple.mobilesafari

Notice what this sequence does not do: it never asks for a booted simulator to exist first. Commands address a target by UDID and leave lifecycle management to whoever is sequencing the commands, which is exactly the separation that makes sharding possible.

The frameworks underneath are usable on their own

`idb` is a front end over two macOS frameworks that live in the same repository: `FBSimulatorControl` and `FBDeviceControl`. The README says plainly that these can be consumed independently, and also that `idb` is probably the better choice for most users because it handles the install and supplies sensible defaults. That is a helpful admission for anyone who needs a lower-level API, since it tells you the framework layer is not an implementation detail you are forbidden to touch.

The repository tree shows how much surface there is. Alongside those two control frameworks you get `FBControlCore`, `Forward/` for port forwarding, `Shims/` for the injected dylibs, `SimulatorFrameworkBridge/`, `REPL/` and `REPLHost/`, `XCTestBootstrap/`, and a `proto/` directory holding the gRPC definition that the Python client speaks. Test targets mirror that structure with names like `FBSimulatorControlTests/` and `EndToEndTests/`.

The language story is mid-migration and the README says so directly: the companion is Swift, the frameworks are moving off Objective-C, and the architecture documentation at fbidb.io tracks where the migration stands. Codebase direction is worth noting before you build on top of these frameworks, since an Objective-C to Swift rewrite changes headers and call sites.

Building from source needs XcodeGen and a protobuf compiler

The documented prerequisites are current-generation: macOS 15 or newer with Xcode 26.0 or newer, XcodeGen installed through brew, and the protobuf compiler for `idb_companion`. One detail in the README is worth repeating because it usually bites: `build.sh` builds the Swift codegen plugins itself, `protoc-gen-swift` and `protoc-gen-grpc-swift-2`, using the versions and revisions pinned in `Package.resolved`, so the generated code matches the runtime it was generated against.

The build script takes a verb and an optional target list:

bash
./build.sh build
./build.sh build frameworks
./build.sh build idb_companion
./build.sh build FBControlCore
./build.sh help

Output lands under `Build/Products`, with macOS products in `Build/Products/Release` and simulator products in platform-specific `Release-*` directories. A full build assembles a self-contained tree at `Build/Distribution` containing `idb_companion`, `idb-repl`, `sim-video`, SwiftPM resource bundles, and a `Resources/` directory with back-deployment libraries and per-platform shim dylibs such as `libShimulator-iOS.dylib`. The README notes the executables resolve bundled dependencies relative to their own location, so that directory has to stay intact when you copy or relocate it.

Tests use the same script, either wholesale or for one framework, and the Xcode project is generated from `project.yml` via `./build.sh generate`, which means `project.yml` is the file to edit rather than the checked-in project file.

How the Python client gets its gRPC stubs

The client is generated rather than handwritten, and the packaging machinery is worth understanding if you hit a build error on install. `setup.py` defines a custom `build_py` command whose first job is to read `protoc_compiler_template.py`, write an executable shim named `protoc-gen-python_grpc`, and prepend the repository directory to `PATH` so the protobuf plugin resolution finds it. It then invokes `grpc_tools.protoc` against `proto/idb.proto`, emitting Python and gRPC stubs into `build/lib/idb/grpc`.

`pyproject.toml` explains why the modern build path needs help. Its build requirements list `setuptools>=61`, `grpcio-tools>=1.29.0` and `grpclib>=0.4.0`, with a comment stating that `setup.py` generates the gRPC stubs at wheel-build time and that modern pip builds in an isolated environment where the old `setup_requires` mechanism no longer provides those packages. If you install from source behind a proxy or an internal mirror, that isolation is the first thing to suspect when stub generation fails.

The remaining Docker files at the repository root are not about running `idb`. `docker-compose.yml` and the `Dockerfile` build the documentation site, not the client: the compose file runs a `docusaurus` service from a `node:8.11.4` base image, publishes ports 3000 and 35729, and bind-mounts `docs/`, `website/blog`, `website/core` and the rest of the site tree into the container. Do not read those files as an alternative deployment path for the companion.

Editorial conclusion

idb earns its place when you need UI-level iOS automation at a scale Xcode cannot reach alone, since the companion and client split lets a test job on a Linux box drive simulators inside a macOS lab, and it is also the honest answer to the question of which Xcode actions have no scriptable interface. It is a poor fit if your automation is limited to tapping through one simulator, because `xcrun simctl` covers that ground with far less machinery, and you should treat the private framework dependency as a real upgrade risk given macOS 15 and Xcode 26 requirements. Start with `idb list-targets` and `idb list-apps` on a fresh machine, then read the architecture page at fbidb.io to see which parts of the codebase are still Objective-C.

Frequently asked questions

How do you install idb on macOS?

The brew formula installs both the macOS companion and the Python client in one step, with `brew install facebook/fb/idb`. The client can also be installed on its own from PyPI with `pip3 install fb-idb`, and it requires python 3.10 or greater.

What is the difference between the idb companion and the idb client?

The companion is the per-target process that runs on macOS and talks to a simulator or device, while the client is the CLI and Python library that can run anywhere. That split is what allows test shards on a non-Mac machine to drive a pool of simulators.

Does idb work with real iOS devices as well as simulators?

Yes. The `FBDeviceControl` framework covers physical devices alongside `FBSimulatorControl` for simulators, and the README states that the primitives aim to be consistent between the two. Discovery still goes through `idb list-targets`, which reports whether a target is a simulator or a device.

Why does idb rely on private frameworks?

The README names this as one of three principles: Xcode has features that are unavailable outside its user interface, and idb uses the same private frameworks Xcode relies on to expose them to GUI-less automation. The tradeoff is that those internals can change between Xcode versions, which is why idb tracks Xcode releases closely.

Official sources

  1. facebook/idb on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/facebook-idb.svg)](https://hysenlabs.com/projects/facebook-idb)