diff --git a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h index 93b1543c7ba..2eb02eafb85 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h +++ b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h @@ -9,6 +9,7 @@ #import #import +@protocol RCTComponentViewProtocol; @class RCTSurfacePresenterBridgeAdapter; /** @@ -96,6 +97,14 @@ #if RCT_NEW_ARCH_ENABLED @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> *)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). diff --git a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm index fffea76a6c3..c04d5e3fed0 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm +++ b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm @@ -11,6 +11,8 @@ #if RCT_NEW_ARCH_ENABLED #import +#import +#import #import #import #import @@ -24,7 +26,10 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot"; -@interface RCTAppDelegate () { +@interface RCTAppDelegate () < + RCTTurboModuleManagerDelegate, + RCTCxxBridgeDelegate, + RCTComponentViewFactoryComponentProvider> { std::shared_ptr _reactNativeConfig; facebook::react::ContextContainer::Shared _contextContainer; std::shared_ptr _runtimeScheduler; @@ -64,6 +69,7 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot"; self.bridge.surfacePresenter = self.bridgeAdapter.surfacePresenter; [self unstable_registerLegacyComponents]; + [RCTComponentViewFactory currentComponentViewFactory].thirdPartyFabricComponentsProvider = self; #endif NSDictionary *initProps = [self prepareInitialProps]; @@ -163,6 +169,13 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot"; return RCTAppSetupDefaultModuleFromClass(moduleClass); } +#pragma mark - RCTComponentViewFactoryComponentProvider + +- (NSDictionary> *)thirdPartyFabricComponents +{ + return @{}; +} + #pragma mark - New Arch Enabled settings - (BOOL)turboModuleEnabled diff --git a/packages/react-native/React/Fabric/Mounting/RCTComponentViewFactory.h b/packages/react-native/React/Fabric/Mounting/RCTComponentViewFactory.h index 771131d50f4..15eb8f0b4c8 100644 --- a/packages/react-native/React/Fabric/Mounting/RCTComponentViewFactory.h +++ b/packages/react-native/React/Fabric/Mounting/RCTComponentViewFactory.h @@ -16,12 +16,28 @@ NS_ASSUME_NONNULL_BEGIN void RCTInstallNativeComponentRegistryBinding(facebook::jsi::Runtime &runtime); +/** + * Protocol that can be implemented to provide some 3rd party components to Fabric. + * Fabric will check in this map whether there are some components that need to be registered. + */ +@protocol RCTComponentViewFactoryComponentProvider + +/** + * Return a dictionary of third party components where the `key` is the Component Handler and the `value` is a Class + * that conforms to `RCTComponentViewProtocol`. + */ +- (NSDictionary> *)thirdPartyFabricComponents; + +@end + /** * Registry of supported component view classes that can instantiate * view component instances by given component handle. */ @interface RCTComponentViewFactory : NSObject +@property (nonatomic, weak) id thirdPartyFabricComponentsProvider; + /** * Constructs and returns an instance of the class with a bunch of already registered standard components. */ diff --git a/packages/react-native/React/Fabric/Mounting/RCTComponentViewFactory.mm b/packages/react-native/React/Fabric/Mounting/RCTComponentViewFactory.mm index 216c2c0b332..8ed6f9a8319 100644 --- a/packages/react-native/React/Fabric/Mounting/RCTComponentViewFactory.mm +++ b/packages/react-native/React/Fabric/Mounting/RCTComponentViewFactory.mm @@ -112,7 +112,19 @@ static Class RCTComponentViewClassWithName(const char return YES; } - // Fallback 2: Try to use Paper Interop. + // Fallback 2: Ask the provider and check in the dictionary provided + if (self.thirdPartyFabricComponentsProvider) { + // Test whether a provider has been passed to avoid potentially expensive conversions + // between C++ and ObjC strings. + NSString *objcName = [NSString stringWithCString:name.c_str() encoding:NSUTF8StringEncoding]; + klass = self.thirdPartyFabricComponentsProvider.thirdPartyFabricComponents[objcName]; + if (klass) { + [self registerComponentViewClass:klass]; + return YES; + } + } + + // Fallback 3: Try to use Paper Interop. NSString *componentNameString = RCTNSStringFromString(name); if ([RCTLegacyViewManagerInteropComponentView isSupported:componentNameString]) { RCTLogNewArchitectureValidation( @@ -136,7 +148,7 @@ static Class RCTComponentViewClassWithName(const char return YES; } - // Fallback 3: use if component doesn't exist. + // Fallback 4: use if component doesn't exist. auto flavor = std::make_shared(name); auto componentName = ComponentName{flavor->c_str()}; auto componentHandle = reinterpret_cast(componentName); diff --git a/packages/react-native/scripts/cocoapods/new_architecture.rb b/packages/react-native/scripts/cocoapods/new_architecture.rb index 471017aa39f..a6eac31d9c5 100644 --- a/packages/react-native/scripts/cocoapods/new_architecture.rb +++ b/packages/react-native/scripts/cocoapods/new_architecture.rb @@ -70,7 +70,6 @@ class NewArchitectureHelper # Set "RCT_DYNAMIC_FRAMEWORKS=1" if pod are installed with USE_FRAMEWORKS=dynamic # This helps with backward compatibility. if pod_name == 'React-RCTFabric' && ENV['USE_FRAMEWORKS'] == 'dynamic' - Pod::UI.puts "Adding RCT_DYNAMIC_FRAMEWORKS=1 to React-RCTFabric".yellow rct_dynamic_framework_flag = " -DRCT_DYNAMIC_FRAMEWORKS=1" target_installation_result.native_target.build_configurations.each do |config| prev_build_settings = config.build_settings['OTHER_CPLUSPLUSFLAGS'] != nil ? config.build_settings['OTHER_CPLUSPLUSFLAGS'] : "$(inherithed)" diff --git a/packages/rn-tester/RNTester/AppDelegate.mm b/packages/rn-tester/RNTester/AppDelegate.mm index d3f3f355f94..bb1cbe68f73 100644 --- a/packages/rn-tester/RNTester/AppDelegate.mm +++ b/packages/rn-tester/RNTester/AppDelegate.mm @@ -49,6 +49,7 @@ #endif #ifdef RN_FABRIC_ENABLED +#import #import #import #import @@ -60,6 +61,10 @@ #import #endif +#if RCT_NEW_ARCH_ENABLED +#import +#endif + #if DEBUG #ifdef FB_SONARKIT_ENABLED #import @@ -85,6 +90,12 @@ } @end +#if RCT_NEW_ARCH_ENABLED +/// Declare conformance to `RCTComponentViewFactoryComponentProvider` +@interface AppDelegate () +@end +#endif + static NSString *const kRNConcurrentRoot = @"concurrentRoot"; @implementation AppDelegate @@ -110,6 +121,10 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot"; // Appetizer.io params check NSDictionary *initProps = [self prepareInitialProps]; +#if RCT_NEW_ARCH_ENABLED + [RCTComponentViewFactory currentComponentViewFactory].thirdPartyFabricComponentsProvider = self; +#endif + #ifdef RN_FABRIC_ENABLED _bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:_bridge contextContainer:_contextContainer]; @@ -333,4 +348,13 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot"; #endif +#pragma mark - RCTComponentViewFactoryComponentProvider + +#if RCT_NEW_ARCH_ENABLED +- (nonnull NSDictionary> *)thirdPartyFabricComponents +{ + return @{@"RNTMyNativeView" : RNTMyNativeViewComponentView.class}; +} +#endif + @end