From 90fce6398fc12bebd9979497ee7d7ca203badda6 Mon Sep 17 00:00:00 2001 From: Phillip Pan Date: Mon, 24 Mar 2025 11:49:07 -0700 Subject: [PATCH] weakly hold key window for KVO frame listener (#50231) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/50231 Changelog: [Internal] RCTDeviceInfo uses KVO to listen to frame changes in the application's keyWindow. On initialization, it reads the global keyWindow and adds itself as a listener. When RCTDeviceInfo is cleaned up, it reads the global keyWindow again, and removes itself as an observer. However, this makes an assumption that the keyWindow is always the same. This is not always true - for example, when a UIAlert is presented, the OS creates a new temporary keyWindow to host the alert in order to make sure it is the first responder. If the cleanup is called then, the app will crash because there is no RCTDeviceInfo observing it. Another example is the LogBox, which also temporarily creates a new keyWindow. The fix is simple, we can capture a reference to the application's keyWindow on initialization, but make sure it is weakly held as the keyWindow is usually managed by iOS. Then, when we remove the listener, it is always guaranteed it is the window that we are observing. Reviewed By: javache, cipolleschi, realsoelynn Differential Revision: D71667722 fbshipit-source-id: 103baf980b79b413fb29e6c3deff81dae33671c0 --- packages/react-native/React/CoreModules/RCTDeviceInfo.mm | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/react-native/React/CoreModules/RCTDeviceInfo.mm b/packages/react-native/React/CoreModules/RCTDeviceInfo.mm index 8410dcefed9..b3079e3ac7b 100644 --- a/packages/react-native/React/CoreModules/RCTDeviceInfo.mm +++ b/packages/react-native/React/CoreModules/RCTDeviceInfo.mm @@ -30,6 +30,8 @@ using namespace facebook::react; BOOL _isFullscreen; std::atomic _invalidated; NSDictionary *_constants; + + __weak UIWindow *_applicationWindow; } static NSString *const kFrameKeyPath = @"frame"; @@ -41,7 +43,8 @@ RCT_EXPORT_MODULE() - (instancetype)init { if (self = [super init]) { - [RCTKeyWindow() addObserver:self forKeyPath:kFrameKeyPath options:NSKeyValueObservingOptionNew context:nil]; + _applicationWindow = RCTKeyWindow(); + [_applicationWindow addObserver:self forKeyPath:kFrameKeyPath options:NSKeyValueObservingOptionNew context:nil]; } return self; } @@ -141,7 +144,7 @@ RCT_EXPORT_MODULE() [[NSNotificationCenter defaultCenter] removeObserver:self name:RCTBridgeWillInvalidateModulesNotification object:nil]; - [RCTKeyWindow() removeObserver:self forKeyPath:kFrameKeyPath]; + [_applicationWindow removeObserver:self forKeyPath:kFrameKeyPath]; #if TARGET_OS_IOS [[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];