/ios-qa: token-rotation wedge + SwiftUI-gesture limitations on iOS 26 (3 findings)
- Dominant language
- TypeScript
- Stars
- 133k
- Forks
- 19.9k
- Avg merge
- 18h 46m
- Merged PRs (30d)
- 26
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:
```swift
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.
Contributor guide
Assessment
This issue has not been assessed yet.