Skip to content

Commit

Permalink
feat: implement ReactNativeFactory (#46298)
Browse files Browse the repository at this point in the history
Summary:
This PR implements ReactNativeFactory to encapsulate further the logic of creating an instance of React Native for iOS.

This will remove the strong coupling on the RCTAppDelegate and allow us to support Scene Delegate in the future.

The goal is to have a following API:

```objc
self.reactNativeFactory = [[RCTReactNativeFactory alloc] initWithDelegate:self];

UIView *rootView = [self.reactNativeFactory.rootViewFactory viewWithModuleName:self.moduleName
                                                               initialProperties:self.initialProps
                                                                   launchOptions:launchOptions];

// Standard iOS stuff here
```

## Changelog:

[IOS] [ADDED] - implement ReactNativeFactory

Pull Request resolved: #46298

Test Plan: Test out all the methods of AppDelegate

Reviewed By: huntie

Differential Revision: D67451403

Pulled By: cipolleschi

fbshipit-source-id: 9e73cd996ffc27ca1e3e058b45fc899b1637bdba
  • Loading branch information
okwasniewski authored and facebook-github-bot committed Dec 31, 2024
1 parent 8b1f049 commit 081be01
Show file tree
Hide file tree
Showing 8 changed files with 514 additions and 284 deletions.

This file was deleted.

54 changes: 6 additions & 48 deletions packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
#import <React/RCTBridgeDelegate.h>
#import <React/RCTConvert.h>
#import <UIKit/UIKit.h>
#import "RCTArchConfiguratorProtocol.h"
#import "RCTDefaultReactNativeFactoryDelegate.h"
#import "RCTReactNativeFactory.h"
#import "RCTRootViewFactory.h"
#import "RCTUIConfiguratorProtocol.h"

@class RCTBridge;
@protocol RCTBridgeDelegate;
Expand Down Expand Up @@ -58,64 +58,22 @@ NS_ASSUME_NONNULL_BEGIN
(const facebook::react::ObjCTurboModule::InitParams &)params
* - (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
*/
@interface RCTAppDelegate : UIResponder <
UIApplicationDelegate,
UISceneDelegate,
RCTBridgeDelegate,
RCTUIConfiguratorProtocol,
RCTArchConfiguratorProtocol>
@interface RCTAppDelegate : RCTDefaultReactNativeFactoryDelegate <UIApplicationDelegate, UISceneDelegate>

/// The window object, used to render the UViewControllers
@property (nonatomic, strong, nonnull) UIWindow *window;

@property (nonatomic, nullable) RCTBridge *bridge;
@property (nonatomic, strong, nullable) NSString *moduleName;
@property (nonatomic, strong, nullable) NSDictionary *initialProps;
@property (nonatomic, strong, nonnull) RCTRootViewFactory *rootViewFactory;
@property (nonatomic, strong) id<RCTDependencyProvider> dependencyProvider;
@property (nonatomic, strong) RCTReactNativeFactory *reactNativeFactory;

/// If `automaticallyLoadReactNativeWindow` is set to `true`, the React Native window will be loaded automatically.
@property (nonatomic, assign) BOOL automaticallyLoadReactNativeWindow;

@property (nonatomic, nullable) RCTSurfacePresenterBridgeAdapter *bridgeAdapter;

/**
* It creates a `RCTBridge` using a delegate and some launch options.
* By default, it is invoked passing `self` as a delegate.
* You can override this function to customize the logic that creates the RCTBridge
*
* @parameter: delegate - an object that implements the `RCTBridgeDelegate` protocol.
* @parameter: launchOptions - a dictionary with a set of options.
*
* @returns: a newly created instance of RCTBridge.
*/
- (RCTBridge *)createBridgeWithDelegate:(id<RCTBridgeDelegate>)delegate launchOptions:(NSDictionary *)launchOptions;

/**
* It creates a `UIView` starting from a bridge, a module name and a set of initial properties.
* By default, it is invoked using the bridge created by `createBridgeWithDelegate:launchOptions` and
* the name in the `self.moduleName` variable.
* You can override this function to customize the logic that creates the Root View.
*
* @parameter: bridge - an instance of the `RCTBridge` object.
* @parameter: moduleName - the name of the app, used by Metro to resolve the module.
* @parameter: initProps - a set of initial properties.
*
* @returns: a UIView properly configured with a bridge for React Native.
*/
- (UIView *)createRootViewWithBridge:(RCTBridge *)bridge
moduleName:(NSString *)moduleName
initProps:(NSDictionary *)initProps;

/// This method returns a map of Component Descriptors and Components classes that needs to be registered in the
/// new renderer. The Component Descriptor is a string which represent the name used in JS to refer to the native
/// component. The default implementation returns an empty dictionary. Subclasses can override this method to register
/// the required components.
///
/// @return a dictionary that associate a component for the new renderer with his descriptor.
- (NSDictionary<NSString *, Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents;

/// Return the bundle URL for the main bundle.
- (NSURL *__nullable)bundleURL;
- (RCTRootViewFactory *)rootViewFactory;

@end

Expand Down
224 changes: 5 additions & 219 deletions packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,13 @@
*/

#import "RCTAppDelegate.h"
#import <React/RCTColorSpaceUtils.h>
#import <React/RCTLog.h>
#import <React/RCTRootView.h>
#import <React/RCTSurfacePresenterBridgeAdapter.h>
#import <React/RCTUtils.h>
#import <ReactCommon/RCTHost.h>
#include <UIKit/UIKit.h>
#import <objc/runtime.h>
#import <react/featureflags/ReactNativeFeatureFlags.h>
#import <react/featureflags/ReactNativeFeatureFlagsDefaults.h>
#import <react/renderer/graphics/ColorComponents.h>
#import "RCTAppDelegate+Protected.h"
#import "RCTAppSetupUtils.h"
#import "RCTDependencyProvider.h"

Expand All @@ -37,9 +32,6 @@

using namespace facebook::react;

@interface RCTAppDelegate () <RCTComponentViewFactoryComponentProvider, RCTHostDelegate>
@end

@implementation RCTAppDelegate

- (instancetype)init
Expand All @@ -52,16 +44,7 @@ - (instancetype)init

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self _setUpFeatureFlags];

RCTSetNewArchEnabled([self newArchEnabled]);
[RCTColorSpaceUtils applyDefaultColorSpace:self.defaultColorSpace];
RCTAppSetupPrepareApp(application, self.turboModuleEnabled);

self.rootViewFactory = [self createRCTRootViewFactory];
if (self.newArchEnabled || self.fabricEnabled) {
[RCTComponentViewFactory currentComponentViewFactory].thirdPartyFabricComponentsProvider = self;
}
self.reactNativeFactory = [[RCTReactNativeFactory alloc] initWithDelegate:self];

if (self.automaticallyLoadReactNativeWindow) {
[self loadReactNativeWindow:launchOptions];
Expand All @@ -84,45 +67,6 @@ - (void)loadReactNativeWindow:(NSDictionary *)launchOptions
[_window makeKeyAndVisible];
}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
[NSException raise:@"RCTBridgeDelegate::sourceURLForBridge not implemented"
format:@"Subclasses must implement a valid sourceURLForBridge method"];
return nil;
}

- (RCTBridge *)createBridgeWithDelegate:(id<RCTBridgeDelegate>)delegate launchOptions:(NSDictionary *)launchOptions
{
return [[RCTBridge alloc] initWithDelegate:delegate launchOptions:launchOptions];
}

- (UIView *)createRootViewWithBridge:(RCTBridge *)bridge
moduleName:(NSString *)moduleName
initProps:(NSDictionary *)initProps
{
BOOL enableFabric = self.fabricEnabled;
UIView *rootView = RCTAppSetupDefaultRootView(bridge, moduleName, initProps, enableFabric);

rootView.backgroundColor = [UIColor systemBackgroundColor];

return rootView;
}

- (UIViewController *)createRootViewController
{
return [UIViewController new];
}

- (void)setRootView:(UIView *)rootView toRootViewController:(UIViewController *)rootViewController
{
rootViewController.view = rootView;
}

- (void)customizeRootView:(RCTRootView *)rootView
{
// Override point for customization after application launch.
}

#pragma mark - UISceneDelegate

- (void)windowScene:(UIWindowScene *)windowScene
Expand All @@ -133,52 +77,11 @@ - (void)windowScene:(UIWindowScene *)windowScene
[[NSNotificationCenter defaultCenter] postNotificationName:RCTWindowFrameDidChangeNotification object:self];
}

- (RCTColorSpace)defaultColorSpace
{
return RCTColorSpaceSRGB;
}

#pragma mark - New Arch Enabled settings

- (BOOL)newArchEnabled
{
#if RCT_NEW_ARCH_ENABLED
return YES;
#else
return NO;
#endif
}

- (BOOL)turboModuleEnabled
{
return [self newArchEnabled];
}

- (BOOL)fabricEnabled
{
return [self newArchEnabled];
}

- (BOOL)bridgelessEnabled
{
return [self newArchEnabled];
}

- (NSURL *)bundleURL
{
[NSException raise:@"RCTAppDelegate::bundleURL not implemented"
format:@"Subclasses must implement a valid getBundleURL method"];
return nullptr;
}

#pragma mark - RCTHostDelegate

- (void)hostDidStart:(RCTHost *)host
- (RCTRootViewFactory *)rootViewFactory
{
return self.reactNativeFactory.rootViewFactory;
}

#pragma mark - Bridge and Bridge Adapter properties

- (RCTBridge *)bridge
{
return self.rootViewFactory.bridge;
Expand All @@ -191,129 +94,12 @@ - (RCTSurfacePresenterBridgeAdapter *)bridgeAdapter

- (void)setBridge:(RCTBridge *)bridge
{
self.rootViewFactory.bridge = bridge;
self.reactNativeFactory.rootViewFactory.bridge = bridge;
}

- (void)setBridgeAdapter:(RCTSurfacePresenterBridgeAdapter *)bridgeAdapter
{
self.rootViewFactory.bridgeAdapter = bridgeAdapter;
}

#pragma mark - RCTTurboModuleManagerDelegate

- (Class)getModuleClassFromName:(const char *)name
{
#if RN_DISABLE_OSS_PLUGIN_HEADER
return RCTTurboModulePluginClassProvider(name);
#else
return RCTCoreModulesClassProvider(name);
#endif
}

- (std::shared_ptr<TurboModule>)getTurboModule:(const std::string &)name
jsInvoker:(std::shared_ptr<CallInvoker>)jsInvoker
{
return DefaultTurboModules::getTurboModule(name, jsInvoker);
}

- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
{
return RCTAppSetupDefaultModuleFromClass(moduleClass, self.dependencyProvider);
}

#pragma mark - RCTComponentViewFactoryComponentProvider

- (NSDictionary<NSString *, Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents
{
return self.dependencyProvider ? self.dependencyProvider.thirdPartyFabricComponents : @{};
}

- (RCTRootViewFactory *)createRCTRootViewFactory
{
__weak __typeof(self) weakSelf = self;
RCTBundleURLBlock bundleUrlBlock = ^{
RCTAppDelegate *strongSelf = weakSelf;
return strongSelf.bundleURL;
};

RCTRootViewFactoryConfiguration *configuration =
[[RCTRootViewFactoryConfiguration alloc] initWithBundleURLBlock:bundleUrlBlock
newArchEnabled:self.fabricEnabled
turboModuleEnabled:self.turboModuleEnabled
bridgelessEnabled:self.bridgelessEnabled];

configuration.createRootViewWithBridge = ^UIView *(RCTBridge *bridge, NSString *moduleName, NSDictionary *initProps) {
return [weakSelf createRootViewWithBridge:bridge moduleName:moduleName initProps:initProps];
};

configuration.createBridgeWithDelegate = ^RCTBridge *(id<RCTBridgeDelegate> delegate, NSDictionary *launchOptions) {
return [weakSelf createBridgeWithDelegate:delegate launchOptions:launchOptions];
};

configuration.customizeRootView = ^(UIView *_Nonnull rootView) {
[weakSelf customizeRootView:(RCTRootView *)rootView];
};

configuration.sourceURLForBridge = ^NSURL *_Nullable(RCTBridge *_Nonnull bridge)
{
return [weakSelf sourceURLForBridge:bridge];
};

if ([self respondsToSelector:@selector(extraModulesForBridge:)]) {
configuration.extraModulesForBridge = ^NSArray<id<RCTBridgeModule>> *_Nonnull(RCTBridge *_Nonnull bridge)
{
return [weakSelf extraModulesForBridge:bridge];
};
}

if ([self respondsToSelector:@selector(extraLazyModuleClassesForBridge:)]) {
configuration.extraLazyModuleClassesForBridge =
^NSDictionary<NSString *, Class> *_Nonnull(RCTBridge *_Nonnull bridge)
{
return [weakSelf extraLazyModuleClassesForBridge:bridge];
};
}

if ([self respondsToSelector:@selector(bridge:didNotFindModule:)]) {
configuration.bridgeDidNotFindModule = ^BOOL(RCTBridge *_Nonnull bridge, NSString *_Nonnull moduleName) {
return [weakSelf bridge:bridge didNotFindModule:moduleName];
};
}

return [[RCTRootViewFactory alloc] initWithTurboModuleDelegate:self hostDelegate:self configuration:configuration];
}

#pragma mark - Feature Flags

class RCTAppDelegateBridgelessFeatureFlags : public ReactNativeFeatureFlagsDefaults {
public:
bool enableBridgelessArchitecture() override
{
return true;
}
bool enableFabricRenderer() override
{
return true;
}
bool useTurboModules() override
{
return true;
}
bool useNativeViewConfigsInBridgelessMode() override
{
return true;
}
bool enableFixForViewCommandRace() override
{
return true;
}
};

- (void)_setUpFeatureFlags
{
if ([self bridgelessEnabled]) {
ReactNativeFeatureFlags::override(std::make_unique<RCTAppDelegateBridgelessFeatureFlags>());
}
self.reactNativeFactory.rootViewFactory.bridgeAdapter = bridgeAdapter;
}

@end
Original file line number Diff line number Diff line change
@@ -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 <UIKit/UIKit.h>
#import "RCTReactNativeFactory.h"

NS_ASSUME_NONNULL_BEGIN

/**
* Default delegate for RCTReactNativeFactory.
* Contains default implementation of RCTReactNativeFactoryDelegate methods.
*/

@interface RCTDefaultReactNativeFactoryDelegate : UIResponder <RCTReactNativeFactoryDelegate>
@end

NS_ASSUME_NONNULL_END
Loading

0 comments on commit 081be01

Please sign in to comment.