Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 95 additions & 6 deletions native-views/react-native-native-overlay/docs/SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,16 +347,50 @@ not change React ownership. Public APIs, Android/Web, and page scope are unchang
first responder; passive toast/debug entries do not win solely by level.
Keyboard restoration may finish after dismissal completion; callers/tests
requiring keyboard geometry must observe the new keyboard readiness event.
- Removing an entry while the app window has an interactive UIKit presentation
- Delivered-touch retirement extension (Implemented; thirty native checks and
four focused local iOS 27.0 App cases passed):
before showing a global entry, install a passive touch observer on its real
app window and on the entry window. Active and retired entries of that real
host share a touch lifetime, including touches delivered to a lower entry or
the exiting window itself. Other real app windows remain independent, even
within the same scene. Each observer tracks delivered touch identities, never
retains UIEvent objects, never cancels or delays touches, and neither prevents
nor can be prevented by another recognizer. It stays possible until the last
tracked touch ends or cancels, then fails and resets without recognizing.
It accepts concurrent touch types with `requiresExclusiveTouchType = false`
and preserves UIKit's default allowed touch types.
- Removing an entry while its real app window or any active/retired entry owned
by that real host has delivered active touches or
an interactive UIKit presentation
removes that entry from active ordering and restores key ownership immediately,
but retains its exiting window/root/scene until the interaction finishes or
cancels. After UIKit reports the interaction change, cleanup runs on the next
main turn and rechecks the current transition coordinator before releasing
but retains its exiting window/root/scene, without hiding it, until both the
delivered touch set is empty and the interaction finishes or cancels. Touch
completion/cancellation and UIKit interaction changes schedule cleanup on the
next main turn. Cleanup rechecks the complete current touch set and transition
coordinator before releasing
that exact window lifetime. A newly interactive transition keeps it retired
until its own completion; repeated or late callbacks cannot release a new
entry for the same owner. Scene disconnect releases active and retired windows
entry for the same owner. A new touch beginning after a previous touch ends but
before queued cleanup keeps the window retired. Overlapping multi-touch can
extend retirement without a timeout. When neither condition exists, removal
still releases synchronously. Remove an entry observer only when its window
is released; remove a real app window's observer only when that host has no
active or retired entries. Touch callbacks query current host ownership across
scenes, rather than binding to the scene of first installation. Moving the
same real host to another scene keeps its remaining users protected.
Reassigning an observed window to a different host does not change its
delivered touch context: retain weak prior-host associations until a next-turn
check finds its complete touch set empty and those hosts' retirements released.
A new touch during an idle gap still protects a prior host whose context remains
retired. Touch completion, observer removal and host reassignment wake current
and prior hosts; queued cleanup does not require the source observer to survive.
Scene disconnect removes that scene's records and unregisters only observers with
no remaining users before releasing active and retired windows
without notifying an already removed owner again. No fixed timeout controls
this handoff.
Key restoration MUST NOT activate a host that has moved to another scene.
Public UIKit observation cannot account for a DOWN not yet delivered to the
app window; acceptance must separately exercise that delivery/teardown boundary.
- Every presentation lifetime has a generation. Asynchronous present, dismiss,
and animation completions must match the generation, controller/entry
identity and applicable phase before changing state or emitting callbacks.
Expand Down Expand Up @@ -692,10 +726,57 @@ More reopened and a subsequent full drag dismissed normally. Home and Market
remained interactive. This checks the main-UI window handoff only; physical
devices and the broader runtime matrix remain unverified.

Historical app-touch acceptance for revision `404e87c` (2026-11-13,
local Debug build on an isolated
iOS 27.0 simulator): all eight first-pan-only cases passed, starting from the
Home header or list body at 250, 300, 350 and 400 ms after More close UP. The
250/300 ms cases kept all twenty old-context events routed to the app window;
350/400 ms started after teardown (observed 5–49 ms later) and routed all twenty
new-context events normally. No backing-event-only dispatch occurred. Native
pager logs recorded ended state 3 in every case. List-body accessibility samples
continued moving after UP without another input, confirming inertia. After
waiting for release, the first React control tap opened Home More (header) or
AccountSelectorStack (body), without another pan or tab switch. Per-case main
and background runtime markers remained unchanged. These measured teardown
boundaries do not guarantee observation of a DOWN UIKit has not delivered yet.

For the same historical revision, two additional selector handoffs exercised
multiple front-card drags and a partial rebound, then front dismissal followed
250 ms later by either a complete
back-selector dismissal or a partial drag canceled into rebound. Both preserved
complete old-context delivery (36 and 16 events respectively), zero backing-only
events, stable main/background markers, and working direct React controls.
The entry was the watch-only account-row More inside AccountSelectorStack,
which uses the same global NativeOverlay UIWindow over a UIKit modal host.
It is an equivalent-host regression, not a replay of the original three-account
HD wallet-level menu. Release/TestFlight binaries, physical devices and other
OS versions remain unverified.

Review extension acceptance for `c59572b`: thirty focused native checks passed.
A rebuilt local Debug App passed first-only header/250 ms and body/300 ms Home
handoffs with native ended state 3, direct React control actions and stable
main/background runtime markers. Two further cases used an independently mounted
passive global entry over Home More. A lower-entry drag received all sixteen
events in its own context while the upper entry closed; its next React close
action worked. This does not establish use of the upper entry's context by the
lower drag. In the exiting-entry case, UIKit delivered DOWN to the upper window,
restored key ownership to the lower window on close, and delivered UP to the
same upper window/context 657 ms after native close; the next lower React close
action worked. Runtime markers stayed stable throughout these four post-recovery
cases. The earlier nineteen native checks, eight Home cases and two equivalent
selector handoffs above belong to `404e87c`, not a rerun of the review version.
The eleven added native groups cover lower/exiting-entry and cross-window touch
ownership, mixed delivered types, repeated host reassignment, UP/rehost/DOWN and
coordinator-held idle gaps, projected scene ownership/disconnect, and key scope.
Mixed-type tests directly deliver synthetic touches and assert observer flags;
they do not prove physical Pencil input. Scene tests project one real scene into
controlled manager identity buckets using an internal resolver; they do not prove
system multi-scene migration. The default resolver remains `ObjectIdentifier(scene)`.

Automated (P0):

- `tests/ios/run.sh` compiles the production WindowManager, SheetController and
Animation into an isolated UIKit test app. Its eleven checks cover bounded/live
Animation into an isolated UIKit test app. Its original nineteen checks cover bounded/live
ordering, key restoration eligibility, owning-entry release, injected scene
disconnect/reuse with original-window identity and reentrant-registration
rejection, conditional original-input focus restoration (including
Expand All @@ -705,6 +786,14 @@ Automated (P0):
retired, and disconnecting a scene with only a retired window. They use a
controlled transition coordinator to test manager lifecycle; the real
gesture check is the iOS 27.0 simulator run above.
Eight app-touch checks directly drive the production observer's
`touchesBegan`/`touchesEnded`/`touchesCancelled` and reset with synthetic
touch objects. They assert passive gesture flags, touch-only retirement,
multi-touch and per-real-window isolation, next-turn touch-set rechecks,
reset cleanup, conjunction with coordinator ownership, owner/orphan reuse,
and scene-disconnect callback invalidation. They prove manager lifecycle,
not UIKit delivery or scroll inertia; those are covered separately by the
local App runtime checks above, with the stated selector-entry limitation.
Sheet checks use `animated: false`; they do not compile the React/Fabric
Container or replace Gallery coverage of animated reversal/generation races,
real multi-scene/disconnect, keyboard geometry or accessibility.
Expand Down
Loading
Loading