fix(ios): serialize Fabric Modal presentation and dismissal - #58583
nahooni0511 wants to merge 1 commit into
Conversation
|
Hi @nahooni0511! Thank you for your pull request and welcome to our community. Action RequiredIn order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you. ProcessIn order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA. Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks! |
|
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks! |
Summary:
Reopening a single iOS Fabric
Modalwhile its animated dismissal is still running can leave an emptyRCTFabricModalHostViewControllerpresented above the application, blocking all touches even after React rendersvisible={false}.The minimal sequence is
trueat 0ms,falseat 600ms,trueat 650ms, andfalseat 1000ms withanimationType="fade". The base implementation reproduces this in the simulator. UIKit retains an appeared, full-screen controller with no child views, while its detached Fabric host has_isPresented == NOand_viewController == nil.This change:
onDismissor restore outside accessibility focus if that dismissal is immediately followed by a queued reopening. This is intentional: the JS Modal'sonDismissclears its rendered state, which otherwise belongs to the newer visibility request. The final dismissal still emitsonDismiss.Related: #50152; single-modal reproducer and native state evidence. This PR targets the single-host transition race, not general stacked-modal support, and does not claim to resolve every multi-modal variant in that issue.
Changelog:
[IOS] [FIXED] - Prevent an invisible Fabric Modal from blocking touches when visibility changes during presentation or dismissal.
Test Plan:
Tested on an iPhone 15 Pro simulator running iOS 27.0, Xcode 27.0 (27A266a), arm64 Release.
Native regression tests: compiled the complete changed component and the test file in a targeted XCTest harness, against available RN 0.86.3/Hermes prebuilt dependencies. The component is renamed at compilation to coexist with the framework's original class; its implementation is included directly from this PR source.
d2a6ce5: 8 failing tests (13 assertion failures). The three existing nested-controller tests and synchronous transition test pass.Actual UIKit/JS reproduction: compiled both the base and patched component into separate simulator-only libraries. A test-only factory hook substitutes either implementation under Fabric's Modal handle in the same Expo 57.0.20 / RN 0.86.3 Release host, with a minimal React/React Native-only entry. Base blocks the counter button; patched accepts the tap. A second sequence keeps the queued reopening visible until 3000ms: it presents again, dismisses, and restores touch access with the patch.
Checks: repository clang-format check for both changed files and
git diff --checkpass.Repro source, complete harness, commands, and screenshots are on a separate evidence branch so they do not expand this PR's source diff. The original reproducer is also included inline in the linked issue comment.
Validation limits: a full RNTester build and all-platform suites were not run. The harness compiles this PR's native implementation, but links RN 0.86.3 prebuilt dependencies rather than rebuilding every dependency from main. Android/tvOS are not runtime-tested. In particular, the queued-reopening
onDismissbehavior and accessibility focus handling warrant maintainer review.