Skip to content

[BUG]: Fix for iOS crash when dismissing modal before view is rendered #74

@Gambitboy

Description

@Gambitboy

Hi! 👋

Firstly, thanks for your work on this project! 🙂

Today I used patch-package to patch react-native-multiple-modals@3.3.0 for the project I'm working on.

Here is the diff that solved my problem:

diff --git a/node_modules/react-native-multiple-modals/android/src/main/java/com/multiplemodals/RNTModalView.kt b/node_modules/react-native-multiple-modals/android/src/main/java/com/multiplemodals/RNTModalView.kt
index 3c1f84f..547753a 100644
--- a/node_modules/react-native-multiple-modals/android/src/main/java/com/multiplemodals/RNTModalView.kt
+++ b/node_modules/react-native-multiple-modals/android/src/main/java/com/multiplemodals/RNTModalView.kt
@@ -162,7 +162,7 @@ class RNTModalView(context: Context): ViewGroup(context), LifecycleEventListener
         return modalView.childCount
     }
 
-    override fun getChildAt(index: Int): View {
+    override fun getChildAt(index: Int): View? {
         return modalView.getChildAt(index)
     }

diff --git a/node_modules/react-native-multiple-modals/ios/Library/RNTModalViewController/RNTModalViewController.m b/node_modules/react-native-multiple-modals/ios/Library/RNTModalViewController/RNTModalViewController.m
index d581919..e31f85a 100644
--- a/node_modules/react-native-multiple-modals/ios/Library/RNTModalViewController/RNTModalViewController.m
+++ b/node_modules/react-native-multiple-modals/ios/Library/RNTModalViewController/RNTModalViewController.m
@@ -22,6 +22,8 @@
 }
 
 - (void)setupReactSubview:(UIView *)subview {
+    if (!self.reactSubviewContainer || !self.view) return;
+    
     [self.view addSubview:self.reactSubviewContainer];
     self.reactSubviewContainer.translatesAutoresizingMaskIntoConstraints = NO;
     
@@ -65,16 +67,23 @@
 
 - (void)dismiss {
     UIView *prevReactSubviewContainer = self.reactSubviewContainer;
-    self.reactSubviewContainer = [self.reactSubviewContainer snapshotViewAfterScreenUpdates:NO];
-    [prevReactSubviewContainer removeFromSuperview];
-
-    [self setupReactSubview:self.reactSubviewContainer];
-    [self.outAnimation prepareAnimation:self.reactSubviewContainer];
-    [self.outAnimation animate:self.reactSubviewContainer completion:^(BOOL finished) {
+    UIView *snapshot = [self.reactSubviewContainer snapshotViewAfterScreenUpdates:NO];
+    
+    if (snapshot) {
+        self.reactSubviewContainer = snapshot;
+        [prevReactSubviewContainer removeFromSuperview];
+        [self setupReactSubview:self.reactSubviewContainer];
+        [self.outAnimation prepareAnimation:self.reactSubviewContainer];
+        [self.outAnimation animate:self.reactSubviewContainer completion:^(BOOL finished) {
+            [self willMoveToParentViewController:nil];
+            [self.view removeFromSuperview];
+            [self removeFromParentViewController];
+        }];
+    } else {
         [self willMoveToParentViewController:nil];
         [self.view removeFromSuperview];
         [self removeFromParentViewController];
-    }];
+    }
 }
 
 - (void)addReactSubview:(UIView *)view {

This issue body was partially generated by patch-package.

I did used claude opus 4.6 to generate this fix, I'm not a swift developer, so I don't know much about the code it generated in terms of convention and how well the fix is. But it did fix the crash I was having.

*edit
I added additional code for android. The same flow crashed on android and I included the kotlin code.

iOS description:
snapshotViewAfterScreenUpdates:NO in RNTModalViewController's dismiss method returns nil when the view hasn't been rendered at least once (iOS logs: "Snapshotting a view that has not been rendered at least once requires afterScreenUpdates:YES"). This sets self.reactSubviewContainer to nil, and the subsequent call to setupReactSubview: crashes when creating layout constraints with nil anchors:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason:
'*** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]'

Android description:
modalView.getChildAt(index) returns View? (nullable in Android) but the Kotlin override declares the return type as View (non-null). When the child doesn't exist, it crashes with the null assertion.

Steps to reproduce:

  1. Rapidly open and close multiple ModalView instances (e.g. a modal that triggers a loading modal on submit)
  2. Repeat 3-5 times (Android crashed after 1 open)
  3. App crashes on iOS & Android

The fix implemented:
iOS:

  • Guard setupReactSubview: against nil reactSubviewContainer or self.view
  • In dismiss, check if snapshotViewAfterScreenUpdates: returned nil — if so, skip the animation and dismiss immediately
    File: ios/Library/RNTModalViewController/RNTModalViewController.m

Android:

  • Change getChildAt return type from View to View? to handle null child during rapid unmount
    Files: RNTModalView.kt

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions