iOS gives an agent one interactive surface, so the only honest background mode is yielding
There is no second screen to put an agent on. What you build instead is a watchdog: a foreground check and a 64 by 64 greyscale diff, running faster than a person can pick up their phone.
An iPhone has one interactive surface and both the owner and the agent want it. Apple's external
display scene role is literally named UIWindowSceneSessionRoleExternalDisplayNonInteractive,
and since iOS 27 shipped on 14 September 2026 an app built against the new SDK is not handed even that
scene from its Info.plist manifest. So the honest version of "run the agent in the background" on iOS
is not a second screen. It is a watchdog that gives the phone back, and ours does it within one 0.8
second poll of the owner touching it.
What changed, and when
External displays on iOS have never been a second place to put an interactive app. The scene role carries "NonInteractive" in its name, and the developer guidance is blunt about the consequence: "this view will not be interactive" (João Gabriel, Cindori, 1 December 2024). Mirroring is a picture of the phone, not a second phone.
iOS 27 tightened it further. osy, the maintainer of UTM, filed pull request 7873 on 16 September 2026 with a one-line description of the breakage: "Apps built with the iOS 27 SDK are no longer offered UIWindowSceneSessionRoleExternalDisplayNonInteractive scenes from the Info.plist scene manifest, so on iOS 27 the external display stayed black while the VM ran on the device." The fix registers the external content as a SwiftUI scene accessory on iOS 27 while iOS 26 and earlier keep the old manifest path. A non-interactive escape hatch got narrower two days after the OS shipped.
| date | what | effect on an agent that wants its own surface | source |
|---|---|---|---|
| 1 Dec 2024 | Current guidance on external display scenes: the role is ...ExternalDisplayNonInteractive | the second display can show pixels, it cannot receive a tap | cindori.com, João Gabriel |
| 14 Sep 2026 | iOS 27 released | ships on iPhone 18 Pro and as an update to iPhone 17 hardware | Wikipedia, iOS 27 |
| 16 Sep 2026 | UTM PR 7873: iOS 27 SDK stops delivering the non-interactive external display scene from the Info.plist manifest | the one remaining second-surface path needs new code per OS version | github.com/utmapp/UTM/pull/7873 |
| this repo | phoneshell/coexist.py: one interactive display, one digitizer, so the agent yields | the design consequence we actually shipped | Blomega/phoneshell |
One caveat on our own note. The coexist.py header records that the iOS 26.4 SDK
headers declare only the non-interactive role and that the interactive one was deprecated in iOS 16.
The machine this note was written on has no iOS SDK installed, so we could not re-read the header
today. The externally checkable half, the constant's name and the iOS 27 manifest change, is cited
above.
The watchdog, with its real constants
If the agent cannot have its own surface, the rule has to be possession in short bursts. The agent
brackets its own work with claim(); anything the watchdog sees outside a claim is
somebody else, which in practice means the person holding the phone.
The threshold is worth stating exactly, because "3% of the screen" sounds vague and is not.
visual_difference converts both screenshots to greyscale, resizes each to 64 by 64, and
counts cells that differ by more than 12 of 255. Above 0.03, that is 123 of 4,096 cells, the watchdog
calls it a change. A blinking cursor does not reach it. Opening the notification shade does.
What this means if you are building one
Three consequences follow, and none of them are about model quality.
Any unattended iPhone task is interruptible by definition, so it needs a resume point, not a retry. A task that assumes uninterrupted possession of someone's personal phone will fail on the first incoming call, and it will fail in a way that looks like an agent error in your logs.
The agent cannot be invisible even for one call. Creating a WebDriverAgent session switches the foreground app, which we measured and wrote up separately, so the first act of any automation run is visible to whoever is holding the phone. Politeness has to be designed in, because stealth is not available.
Screen-change detection has to be cheap enough to run faster than a human reaction. That rules out the accessibility tree, which is the authoritative read and takes about 2.2 seconds on this rig. The cheap pair, a bundle id and a thumbnail diff, is what fits inside the loop. It is less accurate, and that is the correct trade for a watchdog whose only job is to notice that the phone is no longer yours.
Check it yourself
# what the two cheap reads actually cost on your rig
for i in 1 2 3 4 5; do
curl -s -o /dev/null -w "activeAppInfo %{time_total}s\n" http://127.0.0.1:8100/wda/activeAppInfo
curl -s -o /dev/null -w "screenshot %{time_total}s\n" http://127.0.0.1:8100/screenshot
done
curl -s -o /dev/null -w "source %{time_total}s\n" "http://127.0.0.1:8100/source?format=json"
# the constants quoted above
grep -n "poll\|grace\|settle_after_action\|0.03" phoneshell/coexist.py
grep -n "def visual_difference" -A 8 phoneshell/perception/screen.py
For the iOS 27 half, the primary document is the pull request: github.com/utmapp/UTM/pull/7873. Build any app that declares an external display scene in its Info.plist against the iOS 27 SDK, connect a display, and watch it stay black.
What would prove this wrong
The claim is that iOS offers no second interactive surface an agent could occupy while its owner uses the phone. One counterexample retires it: a shipping iOS 27 or iOS 28 API that delivers touch events to a scene the built-in display is not showing, or a WebDriverAgent tap that lands while a finger is on the screen without either input being dropped. If Apple ships an interactive secondary scene role in the iOS 28 betas by June 2027, this note is obsolete and the yielding watchdog becomes a fallback rather than the design.
Sources
- osy, UTM pull request 7873, 16 September 2026: apps built with the iOS 27 SDK are no longer offered
UIWindowSceneSessionRoleExternalDisplayNonInteractivescenes from the Info.plist scene manifest. - Joao Gabriel, Displaying SwiftUI content on an external screen in iOS, Cindori, 1 December 2024: the external display scene role is non-interactive, "this view will not be interactive".
- iOS 27, released 14 September 2026.
- Apple, UIWindowScene, UIKit documentation.
- Blomega/phoneshell,
phoneshell/coexist.py: the 0.8 s poll, the 6 s grace period, the 2.5 s claim and the measured cost of each read. - Blomega/phoneshell,
phoneshell/perception/screen.py:visual_difference, greyscale 64 by 64, cells differing by more than 12 of 255.