fix(ios): deliver simulator edge swipes as screen-edge touches
Summary
On iOS simulators, gesture swipe left-edge/right-edge never started edge recognizers or the navigation edge pop: XCTest touches carry no edge information, even at x = 0.
Edge presets now plan a screen-edge contact (contactOrigin). On an iOS simulator, the runner sends that plan as one HID contact the way a hand swipe arrives: SwipeLocked plus the native swipe direction on every frame, through the oldest touchscreen service of the app's display (the lit panel on a foldable). The contact eases in along the planned path, because a first move that leaves the edge region in one frame fails the recognizer on a wide panel.
- Terminal event: the contact owner builds a cancel event before touch-down. After touch-down, every exit sends the touch-up or that cancel, stamped at dispatch. A cancel that cannot be built stops the command before anything is sent.
- Reported schedule: the runner execution policy resolves the contact and its schedule together and returns
executionProfile(edge-ease-infor this path).gestureResponseDatakeeps a reported schedule and infers the planned profile only when a backend reports none. ADR 0013 recordsedge-ease-inand that rule. Plans still cannot request it.
Other gestures and physical devices keep their paths. 23 files. Fixes #4208.
Validation
Tested at 70bb84f7b, base 035c2b6ca.
pnpm check:affected --run --head 70bb84f7b: passed,exactCommit=true.- Swift unit tests (13,
AGENT_DEVICE_XCUITEST_INCLUDE_UNIT_TESTS=1, iOS 26.4) include HID constructor failures at each frame. Without the cancel dispatch, the move/lift failure test fails. - Repro
./repro.sh, three edge cases plus in-page swipe and pan controls:
| Simulator | Base | Head |
|---|---|---|
| iPhone 16, iOS 26.4 | 0/3 edge | 5/5 |
| iPhone 18 Pro, iOS 27.2 | — | 5/5 |
| iPhone Duo, iOS 27.1, open | 0/3 edge | 5/5 |
| iPhone Duo, iOS 27.1, closed | — | 5/5 |
--jsontiming:right-edge→edge-ease-in,swipe right→endpoint-hold,pan→timed-pan.- An injected cancel delivers
touchesCancelled, and the next touch begins normally.
Open risk: physical devices are unchanged and untested.