iOS UI Automation on Windows with WebDriverAgent on iPhone
Sign, install and start WebDriverAgent on your iPhone with one command, then tap, swipe, type, find elements by text and take screenshots from scripts, test frameworks or AI agents.
Last updated
For iOS UI automation on Windows, one command, kxdevice test runwda, signs WebDriverAgent (WDA, the UI automation server Appium uses on iPhone), installs it on your phone and starts it. Your scripts, test frameworks and AI agents then use local port 8100 to tap, swipe, type, find elements by their text, read the view hierarchy and take screenshots. It works the same on Linux and macOS, and the iPhone needs iOS 17 or later.
What do I need for iOS UI automation on Windows?
Connect your iPhone
The iPhone must trust this computer and have Developer Mode turned on. See Device setup. With several iPhones connected, add --udid to choose one.
Put a development certificate in the shared certificate folder
WDA is signed with the certificates in KXBUILD_HOME/keychain/. A project's own keychain folder isn't used. Add an Apple Development certificate and a development profile that includes this iPhone.
A wildcard profile with App ID * is easiest. A wildcard profile like com.yourcompany.* also works, and WDA then uses the bundle ID com.yourcompany.WebDriverAgentRunner.xctrunner automatically. If you only have a profile for one app, see below. See how to sign iOS apps on Windows.
How do I start WebDriverAgent on my iPhone?
The first run signs, installs and then starts WDA. Later runs just start it. When WDA is ready, the command prints WebDriverAgent is ready at http://127.0.0.1:8100, and your scripts can use that address. The command keeps running. Press Ctrl-C when you're done.
> kxdevice test runwdaYou can also open http://127.0.0.1:8100/status in a browser. If the response contains "ready" : true, WDA is ready.
No wildcard profile? How do I change WDA's bundle ID?
If you don't have a wildcard profile but do have a development profile for another bundle ID that can sign WDA, use --bundle-id to change WDA's bundle ID to that profile's bundle ID.
> kxdevice test runwda --bundle-id com.example.myappWhat options does runwda take?
| Option | When to use it |
|---|---|
| --reinstall | Sign and install again after updating KXBuild, or after changing your certificate or profile |
| --no-install | WDA is already on the iPhone. Start it without checking |
| --ipa <file> | Use a WDA runner you built yourself instead of the bundled one |
| --bundle-id <ID> | Change WDA's bundle ID to this value, then sign and install it. See above |
| --port <port> | Port 8100 on your computer is taken. Pick another, for example --port 8101, and point your scripts at 8101 |
| --no-forward | Don't use a local port. Forward it yourself with kxdevice forward |
| --udid <UDID> | Pick one of several connected iPhones. To drive several at once, give each its own --port |
| -v | Show WDA's own output, for troubleshooting startup |
How do I drive WDA from a Python script?
WDA speaks the WebDriver protocol, so any language that can send HTTP requests can drive it. The Python example below opens Settings, finds General by its text, taps it and takes a screenshot. Elements are found by their visible text or accessibility label, so use the text in the language the app is showing.
> pip install requestsimport base64, requests
WDA = "http://127.0.0.1:8100"
# Open the app and get a session
r = requests.post(f"{WDA}/session", json={
"capabilities": {"alwaysMatch": {"bundleId": "com.apple.Preferences"}}})
sid = r.json()["sessionId"]
# Find an element by its text and tap it
r = requests.post(f"{WDA}/session/{sid}/element",
json={"using": "accessibility id", "value": "General"})
el = r.json()["value"]["ELEMENT"]
requests.post(f"{WDA}/session/{sid}/element/{el}/click")
# Take a screenshot
png = requests.get(f"{WDA}/screenshot").json()["value"]
open("screen.png", "wb").write(base64.b64decode(png))
requests.delete(f"{WDA}/session/{sid}")Replace the bundleId with your own app's. Common requests are below. Prefix each path with http://127.0.0.1:8100.
| To do this | Request |
|---|---|
| Check that WDA is ready | GET /status |
| Open an app and create a session | POST /session with bundleId in capabilities |
| Find an element by text or label | POST /session/<session>/element with using set to accessibility id |
| Tap an element | POST /session/<session>/element/<element>/click |
| Screen size in points | GET /session/<session>/window/size |
| View hierarchy, to look up element names and positions | GET /source |
| Screenshot as a base64-encoded PNG | GET /screenshot |
| Go to the Home Screen | POST /wda/homescreen |
| End the session | DELETE /session/<session> |
If you don't know what an element is called, request /source and look for its name or label attribute in the XML. Swiping, long presses, typing and the other requests are the same as in Appium's WebDriverAgent. See the WebDriverAgent project's documentation.
How do I let an AI agent use the iPhone?
Add kxdevice as an MCP server, and AI agents such as Claude Code and Cursor can tap, swipe, type, find elements by text and read the view hierarchy to check their own UI changes. The first time an agent calls one of these tools, kxdevice starts WDA for it, so you don't run runwda yourself. For example, in Claude Code:
> claude mcp add kxdevice -- kxdevice mcpHow do I fix common WDA errors?
| Problem | Fix |
|---|---|
| Signing or installing fails | Check that KXBUILD_HOME/keychain/ has a development certificate and a development profile that includes this iPhone. Then run again with --reinstall |
| The WebDriverAgent component is missing | Run kxbuild doctor to install the missing component |
| It says iOS 17 is required | WDA only works on iOS 17 or later. Update the iPhone first |
| Port 8100 is in use | Stop the earlier runwda or kxdevice forward that is still running, or pick another port with --port 8101 and use 8101 in your scripts |
| "ready" never appears | Unlock the iPhone first. If that doesn't help, press Ctrl-C and run again with -v to see WDA's output |
| An element can't be found by its text | The text must match what's on screen exactly, including the language. Request /source to see the element's actual name and label. In your own app, set an accessibilityIdentifier on the element |
For startup problems not in the table, rerun runwda with -v to see WDA's own output. Once everything works, the daily loop for iOS UI automation on Windows is simple: run runwda, run your scripts, press Ctrl-C. To debug the app on the device at the same time, see debug iOS apps on iPhone in VS Code.
Frequently asked questions
Which iOS versions does UI automation support?
iPhones on iOS 17 or later. Update older phones first. The iPhone also needs to trust this computer and have Developer Mode turned on.
Do I have to write scripts in Python?
No. WDA speaks the WebDriver protocol, so any language or test framework that can send HTTP requests can drive it. Python is just the example here.
Can I control several iPhones at once?
Yes. Run one runwda per phone, pick each phone with --udid and give each a different --port, such as 8100 and 8101. Each script then talks to its own port.
Does an AI agent need me to run runwda first?
No. Once kxdevice is added as an MCP server, kxdevice starts WDA automatically the first time the agent calls a tool like tap or swipe.
Next steps
kxdevice command reference
Every kxdevice subcommand and option, grouped into devices and pairing, installing and apps, launching and screenshots, debugging and symbols, logs and crashes, files and sandboxes, packet capture and location, backup and diagnostics, and the web UI.
Performance profiling
Profile without Instruments. kxdevice records CPU, memory allocations, network, energy, dropped frames and call stacks straight from your iPhone, from the command line or a web UI.