Control any iOS device or simulator over a simple JSON-RPC API. Tap, swipe, stream video, inspect the UI, from any language, over localhost.
- Interactive automation — Tap, swipe, long-press, type text, press hardware buttons (home, lock, volume)
- App control — Launch, terminate, and detect the foreground app by bundle ID
- Live screen visibility — MJPEG and H264 streaming at configurable FPS, bitrate, and quality
- UI inspection — Full accessibility tree dumps for element targeting
- Screenshots — PNG or JPEG capture with configurable quality
- System control — Get/set orientation, open URLs, query screen size and scale
- Broadcast audio/video — ReplayKit extension with H264 video and Opus audio over TCP
- Flexible transport — JSON-RPC 2.0 over WebSocket or HTTP, from any language
| Platform | Minimum Version |
|---|---|
| iOS | 16.0 |
| Swift | 5.9 |
| Xcode | 15.0+ |
# Clone the repository
git clone https://github.com/mobile-next/devicekit-ios.git
cd devicekit-ios
# Build unsigned IPA for real devices
make ipa-unsigned
# Build XCUITest runner for simulators
make sim-zip| Target | Output | Description |
|---|---|---|
make ipa-unsigned |
build/export/devicekit-ios-unsigned.ipa |
Unsigned IPA for arm64 devices |
make sim-zip-arm64 |
build/export/devicekit-ios-Sim-arm64.zip |
Simulator runner (Apple Silicon) |
make sim-zip-x86_64 |
build/export/devicekit-ios-Sim-x86_64.zip |
Simulator runner (Intel) |
make sim-zip |
Both simulator zips | arm64 + x86_64 |
make lint |
— | Run SwiftLint |
make clean |
— | Remove build artifacts |
Once the server is running at 127.0.0.1:12004, make your first call:
curl -X POST http://127.0.0.1:12004/rpc \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"device.screenshot","params":{},"id":1}'Returns a base64-encoded PNG of the current screen.
DeviceKit runs as an XCUITest. Once installed and launched on a device or simulator, it starts a server on 127.0.0.1:12004.
| Variable | Default | Description |
|---|---|---|
DEVICEKIT_LISTEN_PORT |
12004 |
JSON-RPC server port |
DEVICEKIT_LISTEN_HOST |
127.0.0.1 |
Bind address for the JSON-RPC server. All TCP servers (video, audio) also bind to 127.0.0.1 by default. |
Endpoints:
| Endpoint | Protocol | Description |
|---|---|---|
GET /ws |
WebSocket | JSON-RPC 2.0 |
POST /rpc |
HTTP | JSON-RPC 2.0 |
GET /health |
HTTP | Health check |
GET /mjpeg |
HTTP | MJPEG screen stream |
GET /h264 |
HTTP | H264 screen stream |
All methods follow the JSON-RPC 2.0 specification.
{
"jsonrpc": "2.0",
"method": "device.io.tap",
"params": { "x": 100, "y": 200, "deviceId": "any" },
"id": 1
}| Method | Description |
|---|---|
device.io.tap |
Tap at (x, y) coordinates |
device.io.swipe |
Swipe from (x1, y1) to (x2, y2) |
device.io.longpress |
Long press at (x, y) for a duration |
device.io.gesture |
Multi-finger gesture with press/move/release actions |
device.io.text |
Type text into the focused field |
device.io.button |
Press a hardware button (home, lock, volumeUp, volumeDown) |
| Method | Description |
|---|---|
device.info |
Get screen size and scale factor |
device.io.orientation.get |
Get current orientation (PORTRAIT / LANDSCAPE) |
device.io.orientation.set |
Set orientation to PORTRAIT or LANDSCAPE |
device.url |
Open a URL |
| Method | Description |
|---|---|
device.apps.launch |
Launch an app by bundle ID |
device.apps.terminate |
Terminate an app by bundle ID |
device.apps.foreground |
Get the foreground app's bundle ID, name, and PID |
| Method | Description |
|---|---|
device.dump.ui |
Return the full accessibility view hierarchy |
device.screenshot |
Capture a screenshot (base64 PNG/JPEG) |
GET /mjpeg?fps=10&quality=25&scale=100
| Parameter | Default | Range | Description |
|---|---|---|---|
fps |
10 | 1–60 | Frames per second |
quality |
25 | 1–100 | JPEG quality (%) |
scale |
100 | 10–100 | Scale factor (%) |
GET /h264?fps=30&bitrate=4000000&quality=60&scale=50
| Parameter | Default | Range | Description |
|---|---|---|---|
fps |
30 | 1–60 | Frames per second |
bitrate |
4000000 | 100000–10000000 | Target bitrate (bps) |
quality |
60 | 1–100 | Encoder quality (%) |
scale |
50 | 10–100 | Scale factor (%) |
The ReplayKit broadcast extension provides system-level screen and audio capture over TCP, independent of the JSON-RPC server.
| Port | Stream |
|---|---|
| 12005 | H264 video |
| 12006 | Opus audio |
Tests run against a live XCUITest server on a booted simulator. You need one simulator running — the test harness picks the first booted device automatically.
# Install test dependencies (one-time)
cd tests && npm install && cd ..
# Run tests with code coverage
make test-coverage
# View coverage report as HTML
make coverage-htmldevicekit-ios/
DeviceKit/ # Host app (SwiftUI, triggers broadcast picker)
DeviceKitTests/ # XCUITest runner (automation server)
JSONRPC/ # JSON-RPC protocol + 15 method handlers
Streamer/ # MJPEG and H264 HTTP streaming
XCTest/ # Private API wrappers (touch synthesis, accessibility)
H264Stream/ # Screenshot-based H264 streaming
BroadcastUploadExtension/ # ReplayKit extension (H264 + Opus over TCP)
h264-codec/ # Swift package: H264 encoder (VideoToolbox)
opus-codec/ # Swift package: Opus encoder (wraps libopus)
- FlyingFox — Lightweight HTTP and WebSocket server
- libopus — Audio codec (vendored as C source in
opus-codec/)
Questions, issues, or ideas? Open an issue — all feedback is welcome.
Want to contribute? PRs are appreciated. Check open issues for good first tasks.
DeviceKit iOS is released under the Apache 2.0 License. See LICENSE for details.