Saturday, coffee in hand, the dangerous kind of simple idea in my head.
On my phone sits an app with no API. Agents that can only talk are half an agent.
So I gave Muse, my AI agent, hands on my real iPhone: open the app, read its screens, tap with me in the loop.
The tool, in plain words
The standard tool is WebDriverAgent (WDA): a test-runner app that moonlights as a remote control, signed with your own Apple ID.
Once it runs, the phone becomes a tiny web API on port 8100: GET the screen as XML, POST a tap.
The morning: a "successful" test that never ran
The rig: a current iPhone, an Apple-silicon Mac, Xcode 27.1. The clone was branch 3.17 — vintage code, brand-new iOS 27. The first build hit three walls fast.
Wall 1: deployment target 10.0. Both targets went to 15.0.
Wall 2: warnings-as-errors. Switched OFF.
Wall 3: XCTAutomationSupport links only into approved clients. Fix: a static library, and the client name "XCTest".
By midday the build was green and the test was "running" — 20,301.4 seconds on the record: 5.6 hours of "testing." Nothing was listening on port 8100.
It had never loaded.
The evening: two gates and a missing framework
That evening we stopped trusting the GUI and asked the phone directly.
Gate one: Developer Mode was OFF, so the Developer Disk Image could not mount — a cryptex error, CoreDeviceError 12040/12052. Gate two: trust the developer certificate, or iOS refuses to launch the runner.
Then the real one: a detached launch died instantly (exit code 83). The framework was linked but never embedded.
Two LD_RUNPATH_SEARCH_PATHS lines fixed it, and ServerURLHere printed the phone's address.
iOS 27 removed two methods. We patched both.
iOS 27 had quietly removed two private methods WDA stood on, and WDA called both unguarded.
The first crash came minutes after the server came up: the protocol-version call. Muse read the source, found the send site, and patched it: check first, wrap in @try/@catch, fall back to 0, never crash.
The second surfaced on the first real screen read: the quiescence (app-idle) wait. A settings knob, waitForIdleTimeout, sounds exactly like the fix. We set it to 0. Nothing changed. The knob is decorative. Same patch at the second send site: guard it, catch it, skip the wait if the method is gone.
// The pattern that fixes both iOS 27 crashes: never call a private
// method you have not checked for, and never let the check kill you.
if ([object respondsToSelector:@selector(thePrivateMethod:)]) {
@try {
[object thePrivateMethod:];
} @catch (NSException *exception) {
// Selector vanished mid-flight — fall back. Do not crash the runner.
}
}
The recipe
- Clone the current Appium WebDriverAgent repo, not an old branch; Personal Team signing on both targets, unique bundle IDs (com.yourname.*).
- Deployment target 15.0 on both targets; GCC_TREAT_WARNINGS_AS_ERRORS = NO.
- WebDriverAgentLib: MACH_O_TYPE = staticlib. Runner: link XCTAutomationSupport, add PrivateFrameworks to FRAMEWORK_SEARCH_PATHS, and OTHER_LDFLAGS += "-Wl,-client_name,XCTest".
- Runner: both PrivateFrameworks paths in LD_RUNPATH_SEARCH_PATHS.
- On the phone: Developer Mode ON, then trust the developer certificate.
- Patch both iOS 27 send sites: guard, @try/@catch, fall back.
- Start with xcodebuild test from Terminal: ~90 seconds to server-up. Once, while cabled, tick "Connect via network" in Xcode's Devices window so later starts run over Wi-Fi.
The proof: no cable, one tap
Earlier that evening the patched runner had already proved itself: /status answered ready, carrying the patch's fingerprint: "testmanagerdVersion": 0, and a session survived creation. One landmine, learned then: a session bound to SpringBoard, the home screen, timed out and wedged the server solid. Attach to normal apps.
Then the cable came OUT. Muse drove the phone with plain curl over Wi-Fi and Tailscale, a private mesh VPN, and /status answered ready.
Muse attached to a target app and read its screen live — every label and figure exactly as displayed. Element search by ID was broken on this tree, so Muse took a tab's center, (67, 904), straight from the screen XML and sent one tap; the tab switched on the phone in my hand. The next screen read clean: a list, rows and counts intact.
Why I parked it the same day
The control never missed — a flawless demo. That same evening I told the agent to switch it off. The bill:
While a session runs, the automation screen owns the display. You are carrying a test rig, not a phone.
Every start needs the Mac awake and a human pasting a Terminal command. Muse's attempts to start it by driving Xcode's GUI failed twice; the paste is the only start that ever worked.
Free signing dies every 7 days; $99 a year stretches it to a year.
And the stack is an old branch on two patches.
Two verdicts
Technology verdict and ownership verdict are different verdicts.
If it returns: a spare phone on a dock, next to an always-on Mac — never the daily driver.
Running an agent against your own phone? Tell me where yours broke.
© 2026 Honey Kiran. All rights reserved.
Honey Kiran · DevOps & Platform Engineering
I write about DevOps, platform engineering, Kubernetes, and putting AI agents to work — with guardrails, receipts, and the occasional parked project.
Disclaimer
- This post describes my personal experience with my own systems and accounts, in my own words. It is a first-person account of what I observed and what I do in my own work — not a statement of any company’s intentions, policies, or internal decisions. Where I do not know something, the post says so.
- I am not affiliated with, endorsed by, or speaking for any company, product, or vendor named in this post. All product and company names are trademarks of their respective owners and are mentioned only to describe the experience accurately.
- Any error messages, documentation excerpts, or figures quoted here are reproduced from my own records or from the public sources linked in the post, believed accurate at the time of writing. Product behavior changes over time; verify against current official documentation before relying on any detail.
- Nothing in this post is professional, financial, legal, or security advice for your specific situation. Examples are illustrative — validate in your own environment before running anything in production.
- Corrections are welcome. If any company named here publishes an official statement, or if any detail is wrong, I will update this post and note the correction. The goal is an accurate public record, not a verdict.
Examples are illustrative — validate in your own environment before running in production.






Top comments (0)