From c42884f42cb878b441706852c9dd3c01e159ede3 Mon Sep 17 00:00:00 2001 From: =Raman Date: Wed, 3 Dec 2025 11:58:24 +0530 Subject: [PATCH] feat: Implement UIScene lifecycle for iOS 13+ --- .../Libraries/AppDelegate/RCTAppDelegate.h | 4 ++ .../Libraries/AppDelegate/RCTAppDelegate.mm | 10 +++ .../Libraries/AppDelegate/RCTSceneDelegate.h | 21 ++++++ .../Libraries/AppDelegate/RCTSceneDelegate.mm | 70 +++++++++++++++++++ packages/rn-tester/RNTester/Info.plist | 17 +++++ 5 files changed, 122 insertions(+) create mode 100644 packages/react-native/Libraries/AppDelegate/RCTSceneDelegate.h create mode 100644 packages/react-native/Libraries/AppDelegate/RCTSceneDelegate.mm diff --git a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h index 66d64d22044d..35bd0d6ab6ab 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h +++ b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h @@ -81,6 +81,10 @@ __attribute__((deprecated( - (RCTRootViewFactory *)rootViewFactory; +- (UISceneConfiguration *)application:(UIApplication *)application + configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession + options:(UISceneConnectionOptions *)options API_AVAILABLE(ios(13.0)); + @end NS_ASSUME_NONNULL_END diff --git a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm index 8abc2fc431bf..e042f54ee920 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm +++ b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm @@ -87,4 +87,14 @@ - (void)setBridgeAdapter:(RCTSurfacePresenterBridgeAdapter *)bridgeAdapter self.reactNativeFactory.rootViewFactory.bridgeAdapter = bridgeAdapter; } +- (UISceneConfiguration *)application:(UIApplication *)application + configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession + options:(UISceneConnectionOptions *)options API_AVAILABLE(ios(13.0)) +{ + UISceneConfiguration *configuration = [[UISceneConfiguration alloc] initWithName:@"Default Configuration" + sessionRole:connectingSceneSession.role]; + configuration.delegateClass = NSClassFromString(@"RCTSceneDelegate"); + return configuration; +} + @end diff --git a/packages/react-native/Libraries/AppDelegate/RCTSceneDelegate.h b/packages/react-native/Libraries/AppDelegate/RCTSceneDelegate.h new file mode 100644 index 000000000000..d76108983b9f --- /dev/null +++ b/packages/react-native/Libraries/AppDelegate/RCTSceneDelegate.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import + +@class RCTReactNativeFactory; + +NS_ASSUME_NONNULL_BEGIN + +API_AVAILABLE(ios(13.0)) +@interface RCTSceneDelegate : UIResponder + +@property (nonatomic, strong) UIWindow *window; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/react-native/Libraries/AppDelegate/RCTSceneDelegate.mm b/packages/react-native/Libraries/AppDelegate/RCTSceneDelegate.mm new file mode 100644 index 000000000000..f64c3aa2a47e --- /dev/null +++ b/packages/react-native/Libraries/AppDelegate/RCTSceneDelegate.mm @@ -0,0 +1,70 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "RCTSceneDelegate.h" +#import +#import "RCTAppDelegate.h" +#import "RCTReactNativeFactory.h" +#import "RCTRootViewFactory.h" + +@implementation RCTSceneDelegate + +- (void)scene:(UIScene *)scene + willConnectToSession:(UISceneSession *)session + options:(UISceneConnectionOptions *)connectionOptions API_AVAILABLE(ios(13.0)) +{ + if (![scene isKindOfClass:[UIWindowScene class]]) { + return; + } + + UIWindowScene *windowScene = (UIWindowScene *)scene; + self.window = [[UIWindow alloc] initWithWindowScene:windowScene]; + + // Get the app delegate to access the React Native factory + RCTAppDelegate *appDelegate = (RCTAppDelegate *)[UIApplication sharedApplication].delegate; + + if (appDelegate && [appDelegate respondsToSelector:@selector(rootViewFactory)]) { + RCTRootViewFactory *rootViewFactory = appDelegate.rootViewFactory; + + UIView *rootView = [rootViewFactory viewWithModuleName:appDelegate.moduleName + initialProperties:appDelegate.initialProps + launchOptions:nil]; + + UIViewController *rootViewController = [UIViewController new]; + rootViewController.view = rootView; + self.window.rootViewController = rootViewController; + } + + [self.window makeKeyAndVisible]; +} + +- (void)sceneDidDisconnect:(UIScene *)scene API_AVAILABLE(ios(13.0)) +{ + // Scene disconnected +} + +- (void)sceneDidBecomeActive:(UIScene *)scene API_AVAILABLE(ios(13.0)) +{ + // Scene became active +} + +- (void)sceneWillResignActive:(UIScene *)scene API_AVAILABLE(ios(13.0)) +{ + // Scene will resign active +} + +- (void)sceneWillEnterForeground:(UIScene *)scene API_AVAILABLE(ios(13.0)) +{ + // Scene will enter foreground +} + +- (void)sceneDidEnterBackground:(UIScene *)scene API_AVAILABLE(ios(13.0)) +{ + // Scene entered background +} + +@end diff --git a/packages/rn-tester/RNTester/Info.plist b/packages/rn-tester/RNTester/Info.plist index 373b8322fdc7..b092255e7eda 100644 --- a/packages/rn-tester/RNTester/Info.plist +++ b/packages/rn-tester/RNTester/Info.plist @@ -63,5 +63,22 @@ UIViewControllerBasedStatusBarAppearance + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + RCTSceneDelegate + + + +