Skip to content

Commit

Permalink
feat: refactor RCTKeyWindow to be more resilient and work in multi-…
Browse files Browse the repository at this point in the history
…window apps (facebook#42036)

Summary:
This PRs refactors `RCTKeyWindow()` to be more resilient and work in multi-window apps. After my recent PR got merged (facebook#41935) it significantly reduced the number of calls to `RCTKeyWindow()` and now it's called only when necessary. So this PR makes this function a bit more resource intensive but it guarantees that we will find current scene's key window.

This would also fix some brownfield scenarios where React Native is working in multi-window mode and in the future allow us to more easily adopt `UIWindowSceneDelegate`

bypass-github-export-checks

[IOS] [CHANGED] - refactor `RCTKeyWindow` to be more resilient and work in multi-window apps

Pull Request resolved: facebook#42036

Test Plan:
Checkout RNTester example for Alerts and LoadingView.

https://github.com/facebook/react-native/assets/52801365/8cf4d698-db6d-4a12-8d8d-7a5acf34858b

Reviewed By: huntie

Differential Revision: D52431720

Pulled By: cipolleschi

fbshipit-source-id: 0d6ef1d46b2428c30c9f64dae66b95dbc69f0a3b
  • Loading branch information
okwasniewski authored and Saadnajmi committed Jan 15, 2024
1 parent a0c4294 commit f62ccc8
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 37 deletions.
16 changes: 12 additions & 4 deletions packages/react-native/React/Base/RCTUtils.m
Original file line number Diff line number Diff line change
Expand Up @@ -600,12 +600,20 @@ BOOL RCTRunningInAppExtension(void)
return nil;
}

// TODO: replace with a more robust solution
for (UIWindow *window in RCTSharedApplication().windows) {
if (window.keyWindow) {
return window;
for (UIScene *scene in RCTSharedApplication().connectedScenes) {
if (scene.activationState != UISceneActivationStateForegroundActive ||
![scene isKindOfClass:[UIWindowScene class]]) {
continue;
}
UIWindowScene *windowScene = (UIWindowScene *)scene;

for (UIWindow *window in windowScene.windows) {
if (window.isKeyWindow) {
return window;
}
}
}

return nil;
}

Expand Down
25 changes: 1 addition & 24 deletions packages/react-native/React/CoreModules/RCTAlertController.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,7 @@ @implementation RCTAlertController
- (UIWindow *)alertWindow
{
if (_alertWindow == nil) {
_alertWindow = [self getUIWindowFromScene];

if (_alertWindow == nil) {
UIWindow *keyWindow = RCTSharedApplication().keyWindow;
if (keyWindow) {
_alertWindow = [[UIWindow alloc] initWithFrame:keyWindow.bounds];
} else {
// keyWindow is nil, so we cannot create and initialize _alertWindow
NSLog(@"Unable to create alert window: keyWindow is nil");
}
}
_alertWindow = [[UIWindow alloc] initWithWindowScene:RCTKeyWindow().windowScene];

if (_alertWindow) {
_alertWindow.rootViewController = [UIViewController new];
Expand Down Expand Up @@ -78,19 +68,6 @@ - (void)hide

_alertWindow = nil;
}

- (UIWindow *)getUIWindowFromScene
{
if (@available(iOS 13.0, *)) {
for (UIScene *scene in RCTSharedApplication().connectedScenes) {
if (scene.activationState == UISceneActivationStateForegroundActive &&
[scene isKindOfClass:[UIWindowScene class]]) {
return [[UIWindow alloc] initWithWindowScene:(UIWindowScene *)scene];
}
}
}
return nil;
}
#endif // [macOS]

@end
14 changes: 5 additions & 9 deletions packages/react-native/React/CoreModules/RCTDevLoadingView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,14 @@ - (void)showMessage:(NSString *)message color:(RCTUIColor *)color backgroundColo

dispatch_async(dispatch_get_main_queue(), ^{
self->_showDate = [NSDate date];

if (!self->_window && !RCTRunningInTestEnvironment()) {
#if !TARGET_OS_OSX // [macOS]
UIWindow *window = RCTSharedApplication().keyWindow;
UIWindow *window = RCTKeyWindow();
CGFloat windowWidth = window.bounds.size.width;

self->_window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, windowWidth, window.safeAreaInsets.top + 10)];
self->_window = [[UIWindow alloc] initWithWindowScene:window.windowScene];
self->_window.frame = CGRectMake(0, 0, windowWidth, window.safeAreaInsets.top + 10);
self->_label = [[UILabel alloc] initWithFrame:CGRectMake(0, window.safeAreaInsets.top - 10, windowWidth, 20)];
[self->_window addSubview:self->_label];

Expand Down Expand Up @@ -184,17 +186,11 @@ - (void)showMessage:(NSString *)message color:(RCTUIColor *)color backgroundColo
#else // [macOS
self->_label.stringValue = message;
self->_label.textColor = color;

self->_label.backgroundColor = backgroundColor;
[self->_window orderFront:nil];
#endif // macOS]

#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \
__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
if (@available(iOS 13.0, *)) {
UIWindowScene *scene = (UIWindowScene *)RCTSharedApplication().connectedScenes.anyObject;
self->_window.windowScene = scene;
}
#endif
});
}

Expand Down

0 comments on commit f62ccc8

Please sign in to comment.