react-native icon indicating copy to clipboard operation
react-native copied to clipboard

Application Scene Delegates support in RN >= 0.74

Open DanielKuhn opened this issue 1 year ago • 9 comments

Description

When we added CarPlay support to our React Native app we needed to switch from App Delegate to Application Scene Delegates.

Independently of whether the app was started on the phone (PhoneScene) or on the CarPlay-client (CarScene), the first code to run natively will always be the AppDelegates application:didFinishLaunchingWithOptions: method. A React Native app usually calls the super-method in its AppDelegate, which is implemented in React Native's own RCTAppDelegate. The problem with this is that RCTAppDelegate assumes a phone usage and creates a rootViewController along with a window for the app to be displayed in. This leads to problems when launching the app on the CarPlay-client first, since CarPlay does not require a rootViewController or a window to display its views.

The key to solving this problem is to split the app initialization logic into PhoneScene and CarScene (which are both subclasses of UIResponder) and only run the code required to set up the React Native bridge in the AppDelegate. We can achieve this by not calling the super-method in application:didFinishLaunchingWithOptions: but instead create and call a custom init method.

Prior to React Native 0.74 this wasn't a problem, since all methods needed for setup were publicly exposed. Starting with React Native 0.74, the root view is created via RCTRootViewFactory with no way of instantiating one from the custom initialization routine in App Delegate.

How do you plan to support Application Scene Delegates in the future? Are there any options to create a RCTRootViewFactory without patching the header file as described here? Would it be problematic to expose createRCTRootViewFactory in the header, making it accessible from the App Delegate?

Steps to reproduce

Try setting up a RN 0.74 or 0.75 app via application scene delegates

React Native Version

0.74

Affected Platforms

Runtime - iOS

Output of npx react-native info

irrelevant

Stacktrace or Logs

none

Reproducer

none

Screenshots and Videos

No response

DanielKuhn avatar Aug 23 '24 14:08 DanielKuhn

:warning: Add or Reformat Version Info
:information_source: We could not find or parse the version number of React Native in your issue report. Please use the template, and report your version including major, minor, and patch numbers - e.g. 0.70.2

react-native-bot avatar Aug 23 '24 14:08 react-native-bot

:warning: Missing Reproducible Example
:information_source: We could not detect a reproducible example in your issue report. Please provide either:
  • If your bug is UI related: a Snack
  • If your bug is build/update related: use our Reproducer Template. A reproducer needs to be in a GitHub repository under your username.

react-native-bot avatar Aug 23 '24 14:08 react-native-bot

:warning: Missing Reproducible Example
:information_source: We could not detect a reproducible example in your issue report. Please provide either:

react-native-bot avatar Aug 23 '24 14:08 react-native-bot

:warning: Add or Reformat Version Info
:information_source: We could not find or parse the version number of React Native in your issue report. Please use the template, and report your version including major, minor, and patch numbers - e.g. 0.70.2

react-native-bot avatar Aug 23 '24 14:08 react-native-bot

Hey,

I've been originally working on implementing the RCTRootViewFactory and its goal was to solve the issue you are having.

If you want to completely refactor the initialization flow of React Native, then you can use this class to initialize it however you want. The root view factory encapsulates the logic which prior to RN 0.74 had to be written manually.

Adding support to Scene Delegate is one thing (which I think we should tackle one day) but if you want to use it now you can do it like so:

@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey,id> *)launchOptions {
  
  // Create configuration
 RCTRootViewFactoryConfiguration *configuration = [[RCTRootViewFactoryConfiguration alloc] initWithBundleURL:self.bundleURL 
                                                                                                 newArchEnabled:self.fabricEnabled
                                                                                             turboModuleEnabled:self.turboModuleEnabled
                                                                                              bridgelessEnabled:self.bridgelessEnabled];

// Use blocks to pass callbacks
configuration.sourceURLForBridge = ^NSURL *_Nullable(RCTBridge *_Nonnull bridge)
  {
    
  };
  
  // Initialize RCTRootViewFactory
  self.rootViewFactory = [[RCTRootViewFactory alloc] initWithConfiguration:configuration];
  
  // Create main root view
  UIView *rootView = [self.rootViewFactory viewWithModuleName:@"RNTesterApp" initialProperties:@{} launchOptions:launchOptions];


  
  // Set main window as you prefer for your Brownfield integration.
  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];
  
  // Later in the codebase you can initialize more rootView's using rootViewFactory.
  
  return YES;
}
@end

You can easily refactor the code above to fit into the Scene delegate pattern.

okwasniewski avatar Aug 27 '24 09:08 okwasniewski

This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.

react-native-bot avatar Feb 25 '25 05:02 react-native-bot

Hey, latest React Native (0.78) introduced ReactNativeFactory, so it's now possible to easily switch to SceneDelegate using the factory.

You can reference this file: https://github.com/facebook/react-native/blob/main/packages/helloworld/ios/HelloWorld/AppDelegate.swift

okwasniewski avatar Feb 25 '25 07:02 okwasniewski

Has anyone successfully updated their app to React Native 0.78? I'm struggling to figure out what should go where in the App / Phone / Car delegates.

gavrichards avatar Mar 14 '25 10:03 gavrichards

Also struggling with setting up react native 0.78 + expo with SceneDelegate and car delegate. @gavrichards did you figure out how to make it work?

ouabing avatar May 26 '25 13:05 ouabing