-
Notifications
You must be signed in to change notification settings - Fork 16.3k
/ios-qa: token-rotation wedge + SwiftUI-gesture limitations on iOS 26 (3 findings) #1975
Description
/ios-qa: three findings integrating the debug bridge with a SwiftUI Canvas + DragGesture app on iOS 26
First, thanks for /ios-qa — I drove it end-to-end on a real device (screenshots, state snapshots, and input all working) on a SwiftUI game that renders through TimelineView(.animation) + Canvas and takes its input via a DragGesture. Three things surfaced that I think are worth a look. Code references are against the current templates on main (v1.57.10.0).
Environment: gstack v1.57.10.0 · Xcode 26.5 · iPhone 14, iOS 26.5 · SwiftUI Canvas + TimelineView app.
1. Boot-token rotation scrubs the sandbox file, wedging re-bootstrap on an Xcode 26 tunnel drop (bug)
StateServer.handleAuthRotate (templates/StateServer.swift.template) does, on the first successful rotate:
rotatedToken = newToken bootTokenValid = false // Best-effort scrub of on-disk boot token file. try? FileManager.default.removeItem(atPath: bootTokenPath)
This assumes one bootstrap per app launch. But Xcode 26's CoreDevice drops the USB tunnel between devicectl invocations, so the Mac daemon re-runs bootstrapTunnel (its tunnel cache expires ~30s, or any reconnect). Re-bootstrap calls copyFileFromAppContainer on the boot-token path — which was just deleted — against the still-running app, and fails with boot_token_unavailable. Every reconnect is then wedged until the app is force-relaunched (only a relaunch re-runs start() and rewrites the file).
Repro: rotate once, keep the app running, let the tunnel drop / cache expire, trigger any command → boot_token_unavailable.
Suggested fix: on rotate, re-mint the on-disk token (write a fresh UUID, keep bootTokenValid = true) instead of scrubbing it. That preserves the security property that matters — every previously scraped credential (the os_log boot token, any earlier file copy) is dead the moment rotation lands — while leaving a live file for the daemon to re-read on reconnect. The rotated session token remains the daemon's in-memory secret.
2. In-process synthesized touches don't reach SwiftUI gesture recognizers on iOS 26 (limitation)
DebugBridgeTouch.sendTapAtPoint builds a real UITouch + IOHIDEvent and dispatches via UIApplication.sendEvent. On iOS 26.5 this returns success (hit view found, event delivered) but a SwiftUI DragGesture never fires — a Canvas whose input is a DragGesture sees nothing.
Important: I tried phase-separating the touch (Began → interpolated Moved → Ended across run-loop turns) and it still did not reach the DragGesture, so I don't think the synchronous Began→Ended dispatch is the root cause — it looks like iOS 26's gesture environment doesn't surface in-process synthetic touches to SwiftUI's gesture system at all. (UIKit controls / SwiftUI Button via _UIHitTestContext may still work — I was specifically driving a Canvas + DragGesture.)
Workaround I used: route tap/swipe into the app's own gesture-handler methods (the app exposes its input surface to the debug bridge under #if DEBUG), bypassing synthesis entirely. Deterministic and version-proof.
Open question: is there a known way to drive SwiftUI gestures via in-process synthesis on iOS 26? If not, it might be worth documenting the limitation and/or sanctioning an input-routing hook for gesture-driven apps.
3. /swipe only drives UIScrollView (limitation)
MutationBridgeImpl.handleSwipe (templates/Bridges.swift.template) walks up to the nearest enclosing UIScrollView and nudges setContentOffset; if none is found it returns false (no-op). The code's own comment notes it is "less faithful than synthesized touches but covers common scroll scenarios." For a gesture-driven non-scroll view (SwiftUI Canvas/DragGesture, custom pan handlers), /swipe does nothing.
Suggested direction: synthesize a real touch drag as a fallback when no UIScrollView is found — though per finding #2 that may not reach SwiftUI gestures on iOS 26 anyway, so an input-routing hook may be the more reliable path for those apps.
Happy to open separate issues, send a PR for #1 (the re-mint is a few lines), or share the input-routing approach if useful. Thanks again for the harness.