Skip to content

feat(iOS, FormSheet v5): Introduce PresentationManager for FormSheet component#4086

Open
t0maboro wants to merge 8 commits into
@t0maboro/separate-behavior-from-appearancefrom
@t0maboro/formsheet-presentation-state
Open

feat(iOS, FormSheet v5): Introduce PresentationManager for FormSheet component#4086
t0maboro wants to merge 8 commits into
@t0maboro/separate-behavior-from-appearancefrom
@t0maboro/formsheet-presentation-state

Conversation

@t0maboro
Copy link
Copy Markdown
Contributor

@t0maboro t0maboro commented May 22, 2026

Description

This PR addresses cases where rapid updates to the isOpen prop from JS may cause the native presentation state to desynchronize. Previously, the presentation logic was primitive, and triggering a present or dismiss while another UIKit animation was already in flight would cause UIKit to ignore the call, permanently breaking the component's state.

I'm solving this by introducing a dedicated PresentationManager with a state machine.

  1. The PresentationManager will only fire a present if the current state is Dismissed, and will only fire a dismiss if the state is Presented.
  2. If React toggles the isOpen prop while an animation (state Presenting or Dismissing) is actively running, the manager intentionally suppresses the call to prevent conflicts.
  3. Inside the completion block of every UIKit animation, the manager updates its internal state (from Dismissing to Dismissed, from Presenting to Presented) and then immediately re-evaluates the desired React state. If React updates the value mid-animation, the manager instantly catches up and triggers the next transition.

Note:
The quick isOpen toggle may try to apply the configuration to an old sheetPresentationController that we're going to destroy in Dismissing -> Dismissed transition. I need to force a complete configuration refresh inside prepareForPresentation to guarantee the newly recreated UIKit controller always receives the latest props before it appears on screen.

Closes: https://github.com/software-mansion/react-native-screens-labs/issues/1420

Changes

  • RNSFormSheetPresentationState was defined
  • RNSFormSheetPresentationManager was defined to define the state machine
  • Stripped the presentation logic from RNSFormSheetContentController

Before & after - visual documentation

Before After
before.mov
after.mov

Test plan

Added a new dedicated SFT that will toggle isOpen prop twice with timeout 32ms.

Checklist

  • Included code example that can be used to test this change.
  • For visual changes, included screenshots / GIFs / recordings documenting the change.
  • For API changes, updated relevant public types.
  • Ensured that CI passes

@t0maboro t0maboro changed the base branch from main to @t0maboro/separate-behavior-from-appearance May 22, 2026 13:20
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces a dedicated iOS RNSFormSheetPresentationManager (with a 4-phase state machine) to prevent FormSheet presentation/dismissal calls from desynchronizing when isOpen toggles rapidly during UIKit animations.

Changes:

  • Added RNSFormSheetPresentationState enum and RNSFormSheetPresentationManager to serialize/suppress conflicting transitions and re-evaluate desired state on animation completion.
  • Refactored RNSFormSheetContentController to delegate presentation logic to the manager and to force a full configuration refresh inside prepareForPresentation.
  • Added a new Single Feature Test scenario that stress-toggles isOpen, and registered it in the FormSheet scenario group.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
ios/gamma/modals/form-sheet/RNSFormSheetPresentationState.h New enum defining the manager’s presentation phases.
ios/gamma/modals/form-sheet/RNSFormSheetPresentationManager.h New manager interface for driving presentation transitions.
ios/gamma/modals/form-sheet/RNSFormSheetPresentationManager.mm Manager implementation with guarded present/dismiss and completion re-evaluation.
ios/gamma/modals/form-sheet/RNSFormSheetContentController.h Exposes prepareForPresentation for the manager.
ios/gamma/modals/form-sheet/RNSFormSheetContentController.mm Removes primitive presentation logic; forces configuration refresh during presentation prep; forwards native dismiss to manager.
apps/src/tests/single-feature-tests/form-sheet/test-form-sheet-presentation-state-ios/scenario.md Manual SFT steps for rapid open/close toggling and native dismiss verification.
apps/src/tests/single-feature-tests/form-sheet/test-form-sheet-presentation-state-ios/scenario-description.ts Registers metadata for the new SFT.
apps/src/tests/single-feature-tests/form-sheet/test-form-sheet-presentation-state-ios/index.tsx Implements the rapid toggle UI for the new SFT.
apps/src/tests/single-feature-tests/form-sheet/index.ts Adds the new SFT to the FormSheet scenario group.
Comments suppressed due to low confidence (1)

ios/gamma/modals/form-sheet/RNSFormSheetContentController.mm:10

  • RNSPresentationSourceProvider.h and RCTLog.h are no longer referenced in this file after moving presentation logic to RNSFormSheetPresentationManager. Consider removing these imports to avoid unnecessary dependencies and reduce compile overhead.
#import "RNSFormSheetContentController.h"
#import "RNSFormSheetConfigurationApplicator.h"
#import "RNSFormSheetContentView.h"
#import "RNSFormSheetPresentationManager.h"
#import "RNSFormSheetUpdateCoordinator.h"
#import "RNSFormSheetUpdateFlags.h"
#import "RNSPresentationSourceProvider.h"

#import <React/RCTAssert.h>
#import <React/RCTLog.h>

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread ios/gamma/modals/form-sheet/RNSFormSheetPresentationState.h
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Collaborator

@LKuchno LKuchno left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SFT LGTM ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants