From 30e5b466c27581a6a78041229cef9547e08bce3d Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Tue, 2 Jan 2024 00:41:27 -0800 Subject: [PATCH] Add function to customise RootView in Bridgeless (#42088) Summary: This change adds an extra function to customise the RootView in both Bridge and Bridgeless mode. To nudge users in a migration, we also add a warning message for next version that should push our users to migrate away from the old implementation to the new one. *The Warning is shown ONLY when the user do customise the rootView*. For users which were not customising the Root View, the warning will not appear. The documentation of the new method plus the warning should guide the users toward the right migration path. ## Changelog [iOS][Added] - Added the customiseRootView method which is called in both bridge and bridgeless. Added also a warning for 0.74 with instructions on how to migrate. Reviewed By: cortinico Differential Revision: D52442598 --- .../Libraries/AppDelegate/RCTAppDelegate.h | 21 ++++++++++++++++++ .../Libraries/AppDelegate/RCTAppDelegate.mm | 22 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h index f6a35caf3f0cb3..2c8ce3fa363669 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h +++ b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h @@ -11,6 +11,7 @@ @class RCTBridge; @protocol RCTBridgeDelegate; @protocol RCTComponentViewProtocol; +@class RCTRootView; @class RCTSurfacePresenterBridgeAdapter; /** @@ -86,6 +87,26 @@ - (UIView *)createRootViewWithBridge:(RCTBridge *)bridge moduleName:(NSString *)moduleName initProps:(NSDictionary *)initProps; +/** + * This method can be used to customize the rootView that is passed to React Native. + * A typical example is to override this method in the AppDelegate to change the background color. + * To achieve this, add in your `AppDelegate.mm`: + * ``` + * - (void)customizeRootView:(RCTRootView *)rootView + * { + * rootView.backgroundColor = [UIColor colorWithDynamicProvider:^UIColor *(UITraitCollection *traitCollection) { + * if ([traitCollection userInterfaceStyle] == UIUserInterfaceStyleDark) { + * return [UIColor blackColor]; + * } else { + * return [UIColor whiteColor]; + * } + * }]; + * } + * ``` + * + * @parameter: rootView - The root view to customize. + */ +- (void)customizeRootView:(RCTRootView *)rootView; /** * It creates the RootViewController. diff --git a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm index 65c48ff28d2290..ad8b6625dac5d8 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm +++ b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm @@ -7,6 +7,7 @@ #import "RCTAppDelegate.h" #import +#import #import #import #import @@ -117,6 +118,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( } rootView = [self createRootViewWithBridge:self.bridge moduleName:self.moduleName initProps:initProps]; } + [self customizeRootView:(RCTRootView *)rootView]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; UIViewController *rootViewController = [self createRootViewController]; [self setRootView:rootView toRootViewController:rootViewController]; @@ -144,10 +146,16 @@ - (RCTBridge *)createBridgeWithDelegate:(id)delegate launchOp return [[RCTBridge alloc] initWithDelegate:delegate launchOptions:launchOptions]; } +- (void)customizeRootView:(RCTRootView *)rootView +{ + // Override point for customization after application launch. +} + - (UIView *)createRootViewWithBridge:(RCTBridge *)bridge moduleName:(NSString *)moduleName initProps:(NSDictionary *)initProps { + [self _logWarnIfCreateRootViewWithBridgeIsOverridden]; BOOL enableFabric = self.fabricEnabled; UIView *rootView = RCTAppSetupDefaultRootView(bridge, moduleName, initProps, enableFabric); @@ -156,6 +164,20 @@ - (UIView *)createRootViewWithBridge:(RCTBridge *)bridge return rootView; } +- (void)_logWarnIfCreateRootViewWithBridgeIsOverridden +{ + SEL selector = @selector(createRootViewWithBridge:moduleName:initProps:); + IMP baseClassImp = method_getImplementation(class_getInstanceMethod([RCTAppDelegate class], selector)); + IMP currentClassImp = method_getImplementation(class_getInstanceMethod([self class], selector)); + if (currentClassImp != baseClassImp) { + NSString *warnMessage = + @"If you are using the `createRootViewWithBridge` to customize the root view appearence," + "for example to set the backgroundColor, please migrate to `customiseView` method.\n" + "The `createRootViewWithBridge` method is not invoked in bridgeless."; + RCTLogWarn(@"%@", warnMessage); + } +} + - (UIViewController *)createRootViewController { return [UIViewController new];