Skip to content

Commit

Permalink
feat: implement RCTReactController
Browse files Browse the repository at this point in the history
  • Loading branch information
okwasniewski committed Jan 9, 2024
1 parent e6c1b21 commit e8f630f
Show file tree
Hide file tree
Showing 7 changed files with 397 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ void RCTAppSetupPrepareApp(

RCT_EXTERN_C_BEGIN


// TODO: Rebase with this PR: https://github.com/facebook/react-native/pull/42172/files
//[[deprecated("Use the 3-argument overload of RCTAppSetupPrepareApp instead")]] void RCTAppSetupPrepareApp(
// UIApplication *application,
// BOOL turboModuleEnabled);
Expand Down
56 changes: 56 additions & 0 deletions packages/react-native/Libraries/AppDelegate/RCTReactController.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#import <React/RCTBridgeDelegate.h>
#import <UIKit/UIKit.h>

@class RCTBridge;
@protocol RCTBridgeDelegate;
@protocol RCTComponentViewProtocol;
@class RCTRootView;
@class RCTSurfacePresenterBridgeAdapter;

@protocol RCTAppControllerDelegate<RCTBridgeDelegate>

- (NSURL *)bundleURL;

@end

@interface RCTReactController : UIViewController<RCTAppControllerDelegate>

@property (nonatomic, strong) RCTBridge *bridge;
@property (nonatomic, strong) NSString *moduleName;
@property (nonatomic, strong) NSDictionary *initialProps;
@property (nonatomic, weak) id<RCTBridgeDelegate> delegate;

- (instancetype)initWithModuleName:(NSString*) moduleName;

/// This method controls whether the App will use RuntimeScheduler. Only applicable in the legacy architecture.
///
/// @return: `YES` to use RuntimeScheduler, `NO` to use JavaScript scheduler. The default value is `YES`.
- (BOOL)runtimeSchedulerEnabled;

@property (nonatomic, strong) RCTSurfacePresenterBridgeAdapter *bridgeAdapter;

/// 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;

/// This method controls whether the `turboModules` feature of the New Architecture is turned on or off.
///
/// @note: This is required to be rendering on Fabric (i.e. on the New Architecture).
/// @return: `true` if the Turbo Native Module are enabled. Otherwise, it returns `false`.
- (BOOL)turboModuleEnabled;

/// This method controls whether the App will use the Fabric renderer of the New Architecture or not.
///
/// @return: `true` if the Fabric Renderer is enabled. Otherwise, it returns `false`.
- (BOOL)fabricEnabled;

/// This method controls whether React Native's new initialization layer is enabled.
///
/// @return: `true` if the new initialization layer is enabled. Otherwise returns `false`.
- (BOOL)bridgelessEnabled;

@end
273 changes: 273 additions & 0 deletions packages/react-native/Libraries/AppDelegate/RCTReactController.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
#import "RCTReactController.h"
#import <React/RCTCxxBridgeDelegate.h>
#import <React/RCTLog.h>
#import <React/RCTRootView.h>
#import <React/RCTSurfacePresenterBridgeAdapter.h>
#import <React/RCTUtils.h>
#import <react/renderer/runtimescheduler/RuntimeScheduler.h>
#import "RCTAppSetupUtils.h"
#import <React/RCTUtils.h>

#if RN_DISABLE_OSS_PLUGIN_HEADER
#import <RCTTurboModulePlugin/RCTTurboModulePlugin.h>
#else
#import <React/CoreModulesPlugins.h>
#endif
#import <React/RCTBundleURLProvider.h>
#import <React/RCTComponentViewFactory.h>
#import <React/RCTComponentViewProtocol.h>
#import <React/RCTFabricSurface.h>
#import <React/RCTSurfaceHostingProxyRootView.h>
#import <React/RCTSurfacePresenter.h>
#import <ReactCommon/RCTContextContainerHandling.h>
#if USE_HERMES
#import <ReactCommon/RCTHermesInstance.h>
#else
#import <ReactCommon/RCTJscInstance.h>
#endif
#import <ReactCommon/RCTHost+Internal.h>
#import <ReactCommon/RCTHost.h>
#import <ReactCommon/RCTTurboModuleManager.h>
#import <react/config/ReactNativeConfig.h>
#import <react/renderer/runtimescheduler/RuntimeScheduler.h>
#import <react/renderer/runtimescheduler/RuntimeSchedulerCallInvoker.h>
#import <react/runtime/JSRuntimeFactory.h>

static NSString *kBundlePath = @"js/RNTesterApp.ios";

static NSString *const kRNConcurrentRoot = @"concurrentRoot";

static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabricEnabled)
{
NSMutableDictionary *mutableProps = [initialProps mutableCopy] ?: [NSMutableDictionary new];
// Hardcoding the Concurrent Root as it it not recommended to
// have the concurrentRoot turned off when Fabric is enabled.
mutableProps[kRNConcurrentRoot] = @(isFabricEnabled);
return mutableProps;
}

@interface RCTReactController () <
RCTTurboModuleManagerDelegate,
RCTComponentViewFactoryComponentProvider,
RCTContextContainerHandling> {
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
facebook::react::ContextContainer::Shared _contextContainer;
}
@end

@interface RCTReactController () <RCTCxxBridgeDelegate> {
std::shared_ptr<facebook::react::RuntimeScheduler> _runtimeScheduler;
}
@end

@implementation RCTReactController {
RCTHost *_reactHost;
}


- (instancetype)initWithModuleName:(NSString *)moduleName {
if (self = [super init]) {
_moduleName = moduleName;
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
_contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
}
return self;
}

- (void)loadView {
RCTSetNewArchEnabled([self newArchEnabled]);
BOOL enableTM = self.turboModuleEnabled;
BOOL fabricEnabled = self.fabricEnabled;
BOOL enableBridgeless = self.bridgelessEnabled;

NSDictionary *initProps = updateInitialProps([self prepareInitialProps], fabricEnabled);

RCTAppSetupPrepareApp(nil, enableTM, *_reactNativeConfig);

UIView *rootView;
if (enableBridgeless) {
// Enable native view config interop only if both bridgeless mode and Fabric is enabled.
RCTSetUseNativeViewConfigsInBridgelessMode(fabricEnabled);

// Enable TurboModule interop by default in Bridgeless mode
RCTEnableTurboModuleInterop(YES);
RCTEnableTurboModuleInteropBridgeProxy(YES);

[self createReactHost];
[RCTComponentViewFactory currentComponentViewFactory].thirdPartyFabricComponentsProvider = self;
RCTFabricSurface *surface = [_reactHost createSurfaceWithModuleName:self.moduleName initialProperties:initProps];

RCTSurfaceHostingProxyRootView *surfaceHostingProxyRootView = [[RCTSurfaceHostingProxyRootView alloc]
initWithSurface:surface
sizeMeasureMode:RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightExact];

rootView = (RCTRootView *)surfaceHostingProxyRootView;
} else {
if (!self.bridge) {
self.bridge = [self createBridgeWithDelegate:self launchOptions:@{}];
}
if ([self newArchEnabled]) {
self.bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:self.bridge
contextContainer:_contextContainer];
self.bridge.surfacePresenter = self.bridgeAdapter.surfacePresenter;

[RCTComponentViewFactory currentComponentViewFactory].thirdPartyFabricComponentsProvider = self;
}
rootView = [self createRootViewWithBridge:self.bridge moduleName:self.moduleName initProps:initProps];
}

// Set rootView to the main of this view controller.
self.view = rootView;
}

- (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;
}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
return [self.delegate sourceURLForBridge:bridge];
}

- (BOOL)runtimeSchedulerEnabled
{
return YES;
}

- (NSDictionary *)prepareInitialProps
{
return self.initialProps;
}

#pragma mark - RCTCxxBridgeDelegate
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
{
_runtimeScheduler = std::make_shared<facebook::react::RuntimeScheduler>(RCTRuntimeExecutorFromBridge(bridge));
if ([self newArchEnabled]) {
std::shared_ptr<facebook::react::CallInvoker> callInvoker =
std::make_shared<facebook::react::RuntimeSchedulerCallInvoker>(_runtimeScheduler);
RCTTurboModuleManager *turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
delegate:self
jsInvoker:callInvoker];
_contextContainer->erase("RuntimeScheduler");
_contextContainer->insert("RuntimeScheduler", _runtimeScheduler);
return RCTAppSetupDefaultJsExecutorFactory(bridge, turboModuleManager, _runtimeScheduler);
} else {
return RCTAppSetupJsExecutorFactoryForOldArch(bridge, _runtimeScheduler);
}
}

#pragma mark - New Arch Enabled settings

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

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

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

- (BOOL)bridgelessEnabled
{
return NO;
}

#pragma mark - RCTComponentViewFactoryComponentProvider

- (NSDictionary<NSString *, Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents
{
return @{};
}

#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<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
{
return nullptr;
}

- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
initParams:
(const facebook::react::ObjCTurboModule::InitParams &)params
{
return nullptr;
}

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

#pragma mark - New Arch Utilities

- (void)createReactHost
{
__weak __typeof(self) weakSelf = self;
_reactHost = [[RCTHost alloc] initWithBundleURL:[self bundleURL]
hostDelegate:nil
turboModuleManagerDelegate:self
jsEngineProvider:^std::shared_ptr<facebook::react::JSRuntimeFactory>() {
return [weakSelf createJSRuntimeFactory];
}];
[_reactHost setBundleURLProvider:^NSURL *() {
return [weakSelf bundleURL];
}];
[_reactHost setContextContainerHandler:self];
[_reactHost start];
}

- (std::shared_ptr<facebook::react::JSRuntimeFactory>)createJSRuntimeFactory
{
#if USE_HERMES
return std::make_shared<facebook::react::RCTHermesInstance>(_reactNativeConfig, nullptr);
#else
return std::make_shared<facebook::react::RCTJscInstance>();
#endif
}

- (void)didCreateContextContainer:(std::shared_ptr<facebook::react::ContextContainer>)contextContainer
{
contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
}

- (NSURL *)bundleURL
{
return nullptr;
// return [self.delegate ];
}

@end
31 changes: 30 additions & 1 deletion packages/rn-tester/RNTester-visionOS/App.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,43 @@
//

import SwiftUI
import React

@main
struct NavTestApp: App {
@Environment(\.openWindow) private var openWindow
@UIApplicationDelegateAdaptor var delegate: AppDelegate
let bundleURL = RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "js/RNTesterApp.ios")!

var body: some Scene {
WindowGroup {
Text("Hey")
ReactRootViewRepresentable(moduleName: "RNTesterApp", bundleURL: bundleURL)
.ornament(attachmentAnchor: .scene(.bottom)) {
HStack {
Button("Button 1") {
openWindow(id: "SecondWindow")
}
Button("Button 2") {}
Button("Button 3") {}
Button("Button 4") {}
}
.padding()
.glassBackgroundEffect()
}
}

WindowGroup(id: "SecondWindow") {
ReactRootViewRepresentable(moduleName: "RNTesterApp", bundleURL: bundleURL)
.ornament(attachmentAnchor: .scene(.bottom)) {
HStack {
Button("Button 1") {}
Button("Button 2") {}
Button("Button 3") {}
Button("Button 4") {}
}
.padding()
.glassBackgroundEffect()
}
}
}
}
Loading

0 comments on commit e8f630f

Please sign in to comment.