Describe the bug
On iOS new architecture (Fabric), the card drop-in presents fine on the first checkout but silently fails on every subsequent checkout in the same app session. No JS error, no callback — the pay button appears to do nothing.
Device console shows:
Attempt to present <Adyen.DropInNavigationController: 0x...>
on <RCTFabricModalHostViewController: 0x...>
(from <RCTFabricModalHostViewController: 0x...>)
whose view is not in the window hierarchy.
Same error string as #905, but different cause. #905 is a first-present timing race (Modal onShow). This one is a stale-presenter bug on repeat present: after a prior flow calls router.dismissAll(), its RCTFabricModalHostViewController is torn down on the React side but left presented-yet-detached at the UIKit level. On the next present(), UIViewController.topPresenter walks presentedViewController to the deepest VC and returns that stale, out-of-window host. iOS silently refuses to present on it.
Root cause (library side)
topPresenter's descent loop walks blindly to the deepest presentedViewController with no check that the VC is still live / in-window. Confirmed still present in latest 2.11.1 (loop adopted scene-aware key-window lookup but no detached/dismissing guard).
Steps to reproduce
- RN new arch (Fabric) + expo-router (or any RN modal host).
- Complete/attempt one card checkout (
type: 'scheme').
- Tear down the flow via
router.dismissAll().
- Start a second checkout in the same session → drop-in never appears; console logs the error above.
Physical device only — not reproducible on simulator.
Suggested fix
Guard topPresenter's descent loop to skip detached/dismissing VCs:
while let presented = topController?.presentedViewController,
!presented.isBeingDismissed,
presented.viewIfLoaded?.window != nil {
topController = presented
}
Optionally, in BaseModule.present(), dismiss a leaked presented VC before presenting (belt-and-suspenders):
if let stale = presenter.presentedViewController,
stale.viewIfLoaded?.window == nil || stale.isBeingDismissed {
presenter.dismiss(animated: false) { [weak self] in
self?.present(component)
}
return
}
Environment
@adyen/react-native 2.7.3 (also affects 2.11.1)
- iOS, RN new architecture / Fabric
- Physical device (simulator does not reproduce)
Happy to open a PR with the topPresenter guard if welcome.
Describe the bug
On iOS new architecture (Fabric), the card drop-in presents fine on the first checkout but silently fails on every subsequent checkout in the same app session. No JS error, no callback — the pay button appears to do nothing.
Device console shows:
Same error string as #905, but different cause. #905 is a first-present timing race (Modal
onShow). This one is a stale-presenter bug on repeat present: after a prior flow callsrouter.dismissAll(), itsRCTFabricModalHostViewControlleris torn down on the React side but left presented-yet-detached at the UIKit level. On the nextpresent(),UIViewController.topPresenterwalkspresentedViewControllerto the deepest VC and returns that stale, out-of-window host. iOS silently refuses to present on it.Root cause (library side)
topPresenter's descent loop walks blindly to the deepestpresentedViewControllerwith no check that the VC is still live / in-window. Confirmed still present in latest 2.11.1 (loop adopted scene-aware key-window lookup but no detached/dismissing guard).Steps to reproduce
type: 'scheme').router.dismissAll().Physical device only — not reproducible on simulator.
Suggested fix
Guard
topPresenter's descent loop to skip detached/dismissing VCs:Optionally, in
BaseModule.present(), dismiss a leaked presented VC before presenting (belt-and-suspenders):Environment
@adyen/react-native2.7.3 (also affects 2.11.1)Happy to open a PR with the
topPresenterguard if welcome.