fix app crashes when navigating back from the post screen

This commit is contained in:
Oleksii Bulenok
2026-06-19 13:30:51 +02:00
parent 900a166bf2
commit abd7b8b4cb
4 changed files with 116 additions and 2 deletions
@@ -0,0 +1,66 @@
diff --git a/ios/ExpoPrivacySensitive.podspec b/ios/ExpoPrivacySensitive.podspec
index fa9a44c2668571bb22da7d817960c89d84b37266..ff415bfac2885ed3029b3440c2eb3859f60c0e05 100644
--- a/ios/ExpoPrivacySensitive.podspec
+++ b/ios/ExpoPrivacySensitive.podspec
@@ -2,6 +2,9 @@ require 'json'
package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
+new_arch_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == '1'
+new_arch_compiler_flags = '-DRCT_NEW_ARCH_ENABLED'
+
Pod::Spec.new do |s|
s.name = 'ExpoPrivacySensitive'
s.version = package['version']
@@ -23,6 +26,7 @@ Pod::Spec.new do |s|
# Swift/Objective-C compatibility
s.pod_target_xcconfig = {
'DEFINES_MODULE' => 'YES',
+ 'OTHER_SWIFT_FLAGS' => "$(inherited) #{new_arch_enabled ? new_arch_compiler_flags : ''}",
}
s.source_files = "**/*.{h,m,mm,swift,hpp,cpp}"
diff --git a/ios/ExpoPrivacySensitiveView.swift b/ios/ExpoPrivacySensitiveView.swift
index 499b9b4764894a7ccf7dac2ccee6b09bf14d78cc..424ca9109c32e4cc96faed0a8b8204685899355b 100644
--- a/ios/ExpoPrivacySensitiveView.swift
+++ b/ios/ExpoPrivacySensitiveView.swift
@@ -26,6 +26,31 @@ class ExpoPrivacySensitiveView: ExpoView {
isSettingUp = false
}
+#if RCT_NEW_ARCH_ENABLED
+ // On the new architecture (Fabric), `ExpoView` is an `RCTViewComponentView`
+ // subclass and React children are mounted/unmounted via these methods rather
+ // than `addSubview`/`insertSubview`. We reparent the children into the secure
+ // container here so they stay hidden in screenshots. Crucially, unmount must
+ // not fall through to `super`, whose assertion requires the child to be a
+ // direct subview of `self`; ours live inside `secureContainer`, so unmounting
+ // that way would crash with "Attempt to unmount a view which is mounted inside
+ // different view".
+ override func mountChildComponentView(_ childComponentView: UIView, index: Int) {
+ if let container = secureContainer {
+ container.insertSubview(childComponentView, at: index)
+ } else {
+ super.mountChildComponentView(childComponentView, index: index)
+ }
+ }
+
+ override func unmountChildComponentView(_ childComponentView: UIView, index: Int) {
+ if secureContainer != nil {
+ childComponentView.removeFromSuperview()
+ } else {
+ super.unmountChildComponentView(childComponentView, index: index)
+ }
+ }
+#else
override func addSubview(_ view: UIView) {
if isSettingUp || view === secureContainer {
super.addSubview(view)
@@ -77,6 +102,7 @@ class ExpoPrivacySensitiveView: ExpoView {
super.insertSubview(view, belowSubview: siblingSubview)
}
}
+#endif
override func layoutSubviews() {
super.layoutSubviews()
@@ -0,0 +1,46 @@
# `expo-privacy-sensitive` patch
Makes the native iOS view compatible with the React Native new architecture (Fabric).
## Problem
`ExpoPrivacySensitiveView` hides its content from screenshots by reparenting its
children into a secure `UITextField` container view. It did this by overriding
`addSubview`/`insertSubview`.
On the new architecture, `ExpoView` is an `RCTViewComponentView` subclass, and
Fabric mounts/unmounts React children by calling `mountChildComponentView` /
`unmountChildComponentView` directly on the component view. The mount path went
through the overridden `insertSubview` and reparented the child into the secure
container, but `RCTViewComponentView.unmountChildComponentView` asserts that the
child is a direct subview of `self`. That assertion failed, crashing the app:
```
NSInternalInconsistencyException: Attempt to unmount a view which is mounted
inside different view. (parent: ExpoPrivacySensitive.ExpoPrivacySensitiveView,
child: RCTViewComponentView, index: 0)
```
It reproduced reliably when navigating back from the post thread screen (the
`GrowthHack` component is the only consumer).
## Fix
Two parts, mirroring how `expo-splash-screen` supports both architectures:
1. `ExpoPrivacySensitive.podspec` now adds `-DRCT_NEW_ARCH_ENABLED` to
`OTHER_SWIFT_FLAGS` when `ENV['RCT_NEW_ARCH_ENABLED'] == '1'`. Without this,
the flag is never defined for this pod's Swift, so the `#if` below would
always be false.
2. `ExpoPrivacySensitiveView.swift` gates its implementation on
`#if RCT_NEW_ARCH_ENABLED`: on the new architecture it overrides
`mountChildComponentView` / `unmountChildComponentView` (reparenting on mount,
plain removal on unmount to avoid the superview assertion); the old
`addSubview`/`insertSubview` overrides are kept for the old architecture.
## Important: requires `RCT_NEW_ARCH_ENABLED=1` at install
The podspec opt-in is gated on `ENV['RCT_NEW_ARCH_ENABLED'] == '1'` at
`pod install` time (same as `expo-splash-screen` and `ExpoModulesCore`).
Should be upstreamed to https://github.com/mozzius/expo-privacy-sensitive.