idevice: a pure Rust library for talking to iOS devices
A library to interact with services exposed by iOS
At a glance
- What is it?
- idevice is a Rust library that interfaces with lockdownd, usbmuxd and RSD, so a program can drive an iPhone or iPad the way a Mac would, with each service behind a Cargo feature. The author states plainly that breaking changes will happen at each point release until 0.2.0.
- Who is it for?
- Adopt idevice if you are building a Rust application, server or test harness that must talk to iOS devices, since the async provider model and the feature-per-service layout are what the existing apps needed and could not get elsewhere. Do not adopt it in production without pinning, because the README warns that breaking changes will happen at every point release until 0.2.0.
- 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 last received commits 4 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Why a fourth iOS tooling library exists
idevice is a pure Rust library for interacting with iOS services, inspired by libimobiledevice, pymobiledevice3 and go-ios. Its stated purpose is to interface with lockdownd, usbmuxd and RSD to perform actions on an iOS device that a Mac normally would.
The reason for existing is laid out in the Why use this section, and it is a gap-filling argument rather than a marketing one. libimobiledevice is described as a groundbreaking library that has not been seriously updated in a long time and does not support many modern iOS features. Libraries such as pymobiledevice3 and go-ios have popped up to fill that gap, but both lacked the support needed for embedding into applications and server programs, because Python requires an interpreter and Go's current ability to be embedded in other languages is lacking.
That is a narrow, specific complaint about embeddability, and it explains the shape of the library. A Rust library with no runtime dependency can be linked into a daemon, a desktop application or a test harness without dragging an interpreter or a Go toolchain along.
The README gives the adoption evidence it has: the library is currently used in apps such as StikDebug, CrossCode and Protokolle, and the author states that idevice has proven there is a need and that it is deployed on tens of thousands of devices, all across the world.
The primary language is Rust, the licence is MIT, the default branch is master, and the repository has a Discord for help and information. There is also a DeepWiki badge, which points at generated documentation for the codebase.
The library is in development and research stage, and the README is explicit that releases are published to crates.io for use in other projects, but the API and feature set are far from final or even planned. That sentence sets the expectation for everything else.
Each iOS service behind its own Cargo feature
The feature list is the heart of the design, and the stated goal is to keep dependency bloat and compile time down by containing everything in features.
The list is long and reads like an inventory of Apple's mobile services. afc gives Apple File Conduit file system access, house_arrest manages files in app containers, and mobilebackup2 manages backups. amfi covers the Apple mobile file integrity service. The debugging and diagnostics side includes debug_proxy to send GDB commands to the device, dvt to access Apple developer tools such as Instruments, diagnostics_relay for IORegistry, MobileGestalt, battery, NAND and device control information, and syslog_relay to relay system logs and OS trace logs from the device.
There is also a packet capture pair, pcapd to capture network packets and bt_packet_logger to capture Bluetooth packets. A set of administrative services covers installing and uninstalling apps with installation_proxy, coordinating installations with installcoordination_proxy, managing provisioning profiles with misagent, mounting DDI images with mobile_image_mounter, activating and deactivating the device with mobileactivationd, and handling restore for recovery and reboot with restore_service.
The newer transport surfaces are worth singling out, because they are where recent iOS versions moved. core_device_proxy starts a secure tunnel to access protected services, and xpc accesses protected services over XPC via RSD. There is a matching tunnel_tcp_stack feature, described as a naive in-process TCP stack for core_device_proxy, and a tunneld feature to interface with pymobiledevice3's tunneld. That last one is a candid admission that the two ecosystems meet rather than one replacing the other.
There are also features for things most people never need: companion_proxy to manage paired Apple Watches, location_simulation to simulate GPS locations on the device, preboard_service, heartbeat to maintain a heartbeat connection, springboardservices to control SpringBoard including icons, wallpaper and orientation, screenshotr to take screenshots, xctest to launch XCTest runners and coordinate modern testmanagerd and XCTest sessions, and wda for minimal WebDriverAgent bootstrap helpers and localhost bridge support.
Because every service is opt-in, a program that only needs to read the device's product version and nothing else compiles a small fraction of the library.
The provider model, and what the example code shows
The usage section opens with a design statement: idevice is purposefully verbose to allow for powerful configurations. No size fits all, but effort is made to reduce boilerplate via providers.
The provider is the central abstraction, and the example makes its purpose clear. Many services require opening multiple connections to get where you want, so instead of handing out raw connections the library gives you something that creates them on demand.
// usbmuxd is Apple's daemon for connecting to devices over USB.
// We'll ask usbmuxd for a device
let mut usbmuxd = UsbmuxdConnection::default()
.await
.expect("Unable to connect to usbmuxd");
let devs = usbmuxd.get_devices().unwrap();
if devs.is_empty() {
eprintln!("No devices connected!");
return;
}The connection to a device is then a provider built from a device entry, the usbmuxd address, a device index and a program name.
The verbose part is deliberate. You pass the address and the index and the identifier explicitly rather than having the library guess, which means you can address a specific device when several are plugged in and you can name your client so it shows up in the device's own logs.
The service client is then obtained through a connect method that takes the provider.
The lockdown example in the README shows the two things lockdownd is used for. A get_value call for ProductVersion asks the device for a single value, and get_all_values asks for everything at once. Between them sits start_session, which is given the pairing file obtained from the provider.
The pairing file is the trust anchor. It is what records that this host has been paired with the device, and the provider fetches it for you rather than making you manage it. The example prints the device type as well, which is the usbmuxd-level identifier of the hardware.
More examples live in the tools crate and in the crate documentation, so the README's single snippet is the introduction rather than the whole reference.
The CLI, XCTest and the WebDriverAgent bridge
idevice ships a bundled CLI, idevice-tools, and its most interesting documented use is the xctest command, which starts XCTest runners through the library's modern DVT and RSD path. The README describes this as usable to start WebDriverAgent-style runners on recent iOS versions.
idevice-tools --udid <device-udid> xctest io.github.kor1k1.WebDriverAgentRunner.xctrunnerWith the bridge option, it waits for WebDriverAgent and exposes localhost bridge URLs for HTTP and MJPEG.
idevice-tools --udid <device-udid> xctest --bridge io.github.kor1k1.WebDriverAgentRunner.xctrunnerEnvironment variables are passed to the XCTest runner as comma-separated KEY=VALUE pairs, and when bridging WDA a USE_PORT variable is involved, though the README snippet cuts off there.
What this gives you is a device-side test runner controlled from the command line, over the same library that handles everything else. For a team writing UI tests against real hardware, the practical benefit is that the runner launch, the bridging and the HTTP and MJPEG endpoints all come from the same library that already speaks to the device, so there is one dependency to keep in step rather than two toolchains.
The WebDriverAgent path is the interesting design choice, because the WebDriverAgent project has its own conventions for the runner bundle identifier and its local port, and idevice makes that a command-line argument rather than something you configure in a side file.
The XCTest feature is also the one that most clearly depends on recent iOS versions, since the README ties it to the modern testmanagerd and XCTest session model rather than the older instruments path.
Building the workspace, the FFI and the language bindings
The repository is a Cargo workspace with four members, ffi, idevice, tests and tools, and it has more in it than a Rust library.
The release profile is tuned for size rather than speed. opt-level is set to z, codegen-units is 1, lto is true and panic is abort. Those are the settings you choose when the artefact is going into a mobile app or a size-constrained binary, and they tell you something about the intended embedding target.
The ffi member produces a C interface, and the justfile has a rule that copies the generated header into the Swift directory as part of building an XCFramework, so the C and Swift bindings are built from the same source as the Rust library. The swift/ and cpp/ directories at the top level hold those bindings, with cpp/examples/ and ffi/examples/ each having their own CMake setup.
The CMake examples are built with a standard two-command cmake invocation, using a Release build type and parallel compilation.
cmake -S .. -B . -DCMAKE_BUILD_TYPE=Release
cmake --build . --config Release --parallelThe justfile also holds the feature-checking recipe, which is the tool most relevant to anyone choosing which features to enable. It runs cargo hack over each feature of the idevice package without dev dependencies, pinning the crypto backend to ring and excluding aws-lc, openssl, rustcrypto and the wasm targets.
cargo hack check -p idevice --each-feature --no-dev-deps \
--features ring \
--exclude-features aws-lc,openssl,rustcrypto,wasm,wasm-cryptoThe crypto backend choice is visible in the recipe, which is a detail worth knowing: the library supports several TLS or crypto backends and the CI picks one, so if you enable a different one you are outside what the automated check covers.
The CI targets are split by platform. The Linux and Windows checks build the FFI and the tools natively and run clippy with all targets and all features denying warnings, plus a cargo fmt check. The macOS check adds the XCFramework step, which builds the tools for x86_64-apple-darwin and assembles the Swift package with lipo for the iOS simulator, Mac Catalyst and macOS slices.
Where idevice is the wrong choice
There are three limits, and the first one is stated by the project in the State section.
The first is the stability guarantee, or rather the absence of one. The README states that breaking changes will happen at each point release until 0.2.0, and advises pinning your Cargo.toml to a specific version to avoid breakage. It also says the library is in development and research stage and that the API and feature set are far from final or even planned.
That is an unusually frank statement, and it should be read as a schedule rather than a caveat. Until 0.2.0, each 0.1.x release may move a type, rename a method or change a feature's behaviour. The versions on crates.io are 0.1.68, 0.1.67 and 0.1.66, so the project is deep into the point-release series, and the recent cadence of releases in August and September 2026 suggests the churn is ongoing rather than finished.
The second is coverage. The TODO section lists webinspector as unfinished and file_relay as to be implemented, and the README says the work is done in the author's free time within a busy schedule with no ETA. So there are services in the iOS stack this library does not reach, and the feature list, while long, is a snapshot.
The third is what the library is for. This is device automation infrastructure for development, testing and app-level tooling. Some of the features it exposes, such as location_simulation, debug_proxy and the mobile backup service, are powerful capabilities on a device that belongs to someone else, and the same holds for the C, Swift and C++ bindings, which put the whole surface in reach of code that is not Rust. Whether that is appropriate depends entirely on whether the device is yours, and on the policies of the organisation you are working in.
The honest alternative depends on the constraint. If you need to embed device control in a long-running server and language choice is free, go-ios is the reference implementation in Go with broad service coverage, and libimobiledevice remains the stable C library that other tools build on, at the cost the README describes of not supporting many modern iOS features. If you are writing a test harness and want the shortest path to controlling a device from any language, an Appium or XCUITest based stack gives you the XCTest runner and the WebDriverAgent bridge without writing device transport code at all, and idevice's xctest command is really a way to get the same result without that stack.
If the language is Rust and you want the modern RSD and core_device_proxy paths, idevice is the only one of these options that gives them to you, and the pinning advice in the README is the cost of that choice.
Editorial conclusion
Adopt idevice if you are building a Rust application, server or test harness that must talk to iOS devices, since the async provider model and the feature-per-service layout are what the existing apps needed and could not get elsewhere. Do not adopt it in production without pinning, because the README warns that breaking changes will happen at every point release until 0.2.0. Before you start, read the State section and pin your Cargo.toml to a specific version, and note the last push was on 2026-10-19 with v0.1.68 from 2026-09-15.
Frequently asked questions
What is the idevice Rust library?
It is a pure Rust library for interacting with iOS services, interfacing with lockdownd, usbmuxd and RSD to perform actions on an iOS device that a Mac normally would. It was inspired by libimobiledevice, pymobiledevice3 and go-ios, and the primary goal is embeddability in applications and server programs.
How do I enable a specific iOS service in idevice?
Every service is behind its own Cargo feature to keep dependency bloat and compile time down. The list includes afc for file system access, debug_proxy for GDB commands, dvt for developer tools, installation_proxy for app installation, mobilebackup2 for backups, syslog_relay for logs, core_device_proxy for a secure tunnel and xpc for protected services over RSD.
Is the idevice API stable?
Not yet. The README states that breaking changes will happen at each point release until 0.2.0, advises pinning your Cargo.toml to a specific version to avoid breakage, and says the library is in development and research stage with an API and feature set that are far from final or even planned.
How do I launch an XCTest runner with idevice-tools?
Use the xctest command with the device UDID and the runner bundle identifier, for example idevice-tools --udid <device-udid> xctest io.github.kor1k1.WebDriverAgentRunner.xctrunner. Adding the --bridge flag waits for WebDriverAgent and exposes localhost bridge URLs for HTTP and MJPEG, and environment variables are passed to the runner as comma-separated KEY=VALUE pairs.
What does the idevice provider abstraction do?
The library is purposefully verbose so configurations can be powerful, and effort is made to reduce boilerplate via providers. A provider creates connections on demand, which matters because many services require opening multiple connections, and it carries the usbmuxd address, the device index and a program name so you can address a specific device when several are connected.
Official sources
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.
[](https://hysenlabs.com/projects/jkcoxson-idevice)Was this useful?