From 0cc1ac18cf7f2a99500ef6f315c4dccda7736ea0 Mon Sep 17 00:00:00 2001 From: Vojtech Novak Date: Tue, 11 Mar 2025 05:57:40 -0700 Subject: [PATCH] fix(ios): enable use of multiple RCTAppDependencyProvider instances (#49889) Summary: instantiating multiple `RCTAppDependencyProvider` instances creates problems with their internal state, because the internal fields such as [`_thirdPartyFabricComponents` are populated once](https://github.com/facebook/react-native/blob/f5feb73022f9340583ebcf576eaedd3ca5677e1a/packages/react-native/scripts/codegen/templates/RCTAppDependencyProviderMM.template#L60) for the entire app (see [`dispatch_once`](https://developer.apple.com/documentation/dispatch/1447169-dispatch_once)). That means when you create 2 instances, and call `thirdPartyFabricComponents` on them, the first instance will respond with a dictionary and the second with `nil`. This is unexpected. ## Changelog: [IOS] [FIXED] - enable use of multiple `RCTAppDependencyProvider` instances Pull Request resolved: https://github.com/facebook/react-native/pull/49889 Test Plan: `rnTester` - the templates are used by codegen. Running `pod install`, the files were generated correctly from the `.template` files. I was able to verify that accessing `thirdPartyFabricComponents` on multiple instances of `RCTAppDependencyProvider` returns valid result.
screenshot Screenshot 2025-03-07 at 15 11 22
Reviewed By: javache Differential Revision: D70892779 Pulled By: cipolleschi fbshipit-source-id: 27c7c8fd9982b6427daec02e0de59b08ad20bfad --- .../codegen/generate-artifacts-executor.js | 10 ++--- .../RCTAppDependencyProviderMM.template | 42 +++-------------- .../templates/RCTModuleProvidersMM.template | 45 +++++++++++-------- ...esConformingToProtocolsProviderMM.template | 39 ++++++++++++---- ...RCTThirdPartyComponentsProviderMM.template | 11 ++++- 5 files changed, 76 insertions(+), 71 deletions(-) diff --git a/packages/react-native/scripts/codegen/generate-artifacts-executor.js b/packages/react-native/scripts/codegen/generate-artifacts-executor.js index ee80d91b09f..482436f3a1a 100644 --- a/packages/react-native/scripts/codegen/generate-artifacts-executor.js +++ b/packages/react-native/scripts/codegen/generate-artifacts-executor.js @@ -613,7 +613,7 @@ function generateCustomURLHandlers(libraries, outputDir) { ) .filter(Boolean) .map(className => `@"${className}"`) - .join(',\n\t\t'); + .join(',\n\t\t\t'); const customImageDataDecoderClasses = libraries .flatMap( @@ -622,7 +622,7 @@ function generateCustomURLHandlers(libraries, outputDir) { ) .filter(Boolean) .map(className => `@"${className}"`) - .join(',\n\t\t'); + .join(',\n\t\t\t'); const customURLHandlerClasses = libraries .flatMap( @@ -631,7 +631,7 @@ function generateCustomURLHandlers(libraries, outputDir) { ) .filter(Boolean) .map(className => `@"${className}"`) - .join(',\n\t\t'); + .join(',\n\t\t\t'); const template = fs.readFileSync(MODULES_PROTOCOLS_MM_TEMPLATE_PATH, 'utf8'); const finalMMFile = template @@ -736,7 +736,7 @@ function generateRCTModuleProviders( .flatMap(library => { const modules = modulesInLibraries[library]; return modules.map(({moduleName, className}) => { - return `\t\t@"${moduleName}": @"${className}", // ${library}`; + return `\t\t\t@"${moduleName}": @"${className}", // ${library}`; }); }) .join('\n'); @@ -805,7 +805,7 @@ function generateRCTThirdPartyComponents(libraries, outputDir) { .flatMap(library => { const components = componentsInLibraries[library]; return components.map(({componentName, className}) => { - return `\t\t@"${componentName}": NSClassFromString(@"${className}"), // ${library}`; + return `\t\t\t@"${componentName}": NSClassFromString(@"${className}"), // ${library}`; }); }) .join('\n'); diff --git a/packages/react-native/scripts/codegen/templates/RCTAppDependencyProviderMM.template b/packages/react-native/scripts/codegen/templates/RCTAppDependencyProviderMM.template index 5463cf70309..e19c096e1e7 100644 --- a/packages/react-native/scripts/codegen/templates/RCTAppDependencyProviderMM.template +++ b/packages/react-native/scripts/codegen/templates/RCTAppDependencyProviderMM.template @@ -10,56 +10,26 @@ #import #import -@implementation RCTAppDependencyProvider { - NSArray * _URLRequestHandlerClassNames; - NSArray * _imageDataDecoderClassNames; - NSArray * _imageURLLoaderClassNames; - NSDictionary> * _thirdPartyFabricComponents; - NSDictionary> * _moduleProviders; -} +@implementation RCTAppDependencyProvider - (nonnull NSArray *)URLRequestHandlerClassNames { - static dispatch_once_t requestUrlToken; - dispatch_once(&requestUrlToken, ^{ - self->_URLRequestHandlerClassNames = RCTModulesConformingToProtocolsProvider.URLRequestHandlerClassNames; - }); - - return _URLRequestHandlerClassNames; + return RCTModulesConformingToProtocolsProvider.URLRequestHandlerClassNames; } - (nonnull NSArray *)imageDataDecoderClassNames { - static dispatch_once_t dataDecoderToken; - dispatch_once(&dataDecoderToken, ^{ - _imageDataDecoderClassNames = RCTModulesConformingToProtocolsProvider.imageDataDecoderClassNames; - }); - - return _imageDataDecoderClassNames; + return RCTModulesConformingToProtocolsProvider.imageDataDecoderClassNames; } - (nonnull NSArray *)imageURLLoaderClassNames { - static dispatch_once_t urlLoaderToken; - dispatch_once(&urlLoaderToken, ^{ - _imageURLLoaderClassNames = RCTModulesConformingToProtocolsProvider.imageURLLoaderClassNames; - }); - - return _imageURLLoaderClassNames; + return RCTModulesConformingToProtocolsProvider.imageURLLoaderClassNames; } - (nonnull NSDictionary> *)thirdPartyFabricComponents { - static dispatch_once_t nativeComponentsToken; - dispatch_once(&nativeComponentsToken, ^{ - _thirdPartyFabricComponents = RCTThirdPartyComponentsProvider.thirdPartyFabricComponents; - }); - - return _thirdPartyFabricComponents; + return RCTThirdPartyComponentsProvider.thirdPartyFabricComponents; } - (nonnull NSDictionary> *)moduleProviders { - static dispatch_once_t modulesToken; - dispatch_once(&modulesToken, ^{ - _moduleProviders = RCTModuleProviders.moduleProviders; - }); - return _moduleProviders; + return RCTModuleProviders.moduleProviders; } @end diff --git a/packages/react-native/scripts/codegen/templates/RCTModuleProvidersMM.template b/packages/react-native/scripts/codegen/templates/RCTModuleProvidersMM.template index 2b580788953..b1fb2c72505 100644 --- a/packages/react-native/scripts/codegen/templates/RCTModuleProvidersMM.template +++ b/packages/react-native/scripts/codegen/templates/RCTModuleProvidersMM.template @@ -15,30 +15,37 @@ + (NSDictionary> *)moduleProviders { - NSDictionary * moduleMapping = @{ - {moduleMapping} - }; + static NSDictionary> *providers = nil; + static dispatch_once_t onceToken; - NSMutableDictionary *dict = [NSMutableDictionary new]; + dispatch_once(&onceToken, ^{ + NSDictionary * moduleMapping = @{ + {moduleMapping} + }; - for (NSString *key in moduleMapping) { - NSString * moduleProviderName = moduleMapping[key]; - Class klass = NSClassFromString(moduleProviderName); - if (!klass) { - RCTLogError(@"Module provider %@ cannot be found in the runtime", moduleProviderName); - continue; + NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:moduleMapping.count]; + + for (NSString *key in moduleMapping) { + NSString * moduleProviderName = moduleMapping[key]; + Class klass = NSClassFromString(moduleProviderName); + if (!klass) { + RCTLogError(@"Module provider %@ cannot be found in the runtime", moduleProviderName); + continue; + } + + id instance = [klass new]; + if (![instance respondsToSelector:@selector(getTurboModule:)]) { + RCTLogError(@"Module provider %@ does not conform to RCTModuleProvider", moduleProviderName); + continue; + } + + [dict setObject:instance forKey:key]; } - id instance = [klass new]; - if (![instance respondsToSelector:@selector(getTurboModule:)]) { - RCTLogError(@"Module provider %@ does not conform to RCTModuleProvider", moduleProviderName); - continue; - } + providers = dict; + }); - [dict setObject:instance forKey:key]; - } - - return dict; + return providers; } @end diff --git a/packages/react-native/scripts/codegen/templates/RCTModulesConformingToProtocolsProviderMM.template b/packages/react-native/scripts/codegen/templates/RCTModulesConformingToProtocolsProviderMM.template index 1db7f6cb57a..7a432b968ca 100644 --- a/packages/react-native/scripts/codegen/templates/RCTModulesConformingToProtocolsProviderMM.template +++ b/packages/react-native/scripts/codegen/templates/RCTModulesConformingToProtocolsProviderMM.template @@ -11,23 +11,44 @@ +(NSArray *)imageURLLoaderClassNames { - return @[ - {imageURLLoaderClassNames} - ]; + static NSArray *classNames = nil; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + classNames = @[ + {imageURLLoaderClassNames} + ]; + }); + + return classNames; } +(NSArray *)imageDataDecoderClassNames { - return @[ - {imageDataDecoderClassNames} - ]; + static NSArray *classNames = nil; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + classNames = @[ + {imageDataDecoderClassNames} + ]; + }); + + return classNames; } +(NSArray *)URLRequestHandlerClassNames { - return @[ - {requestHandlersClassNames} - ]; + static NSArray *classNames = nil; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + classNames = @[ + {requestHandlersClassNames} + ]; + }); + + return classNames; } @end diff --git a/packages/react-native/scripts/codegen/templates/RCTThirdPartyComponentsProviderMM.template b/packages/react-native/scripts/codegen/templates/RCTThirdPartyComponentsProviderMM.template index 34f84bc5c6e..4ce510709e3 100644 --- a/packages/react-native/scripts/codegen/templates/RCTThirdPartyComponentsProviderMM.template +++ b/packages/react-native/scripts/codegen/templates/RCTThirdPartyComponentsProviderMM.template @@ -15,9 +15,16 @@ + (NSDictionary> *)thirdPartyFabricComponents { - return @{ + static NSDictionary> *thirdPartyComponents = nil; + static dispatch_once_t nativeComponentsToken; + + dispatch_once(&nativeComponentsToken, ^{ + thirdPartyComponents = @{ {thirdPartyComponentsMapping} - }; + }; + }); + + return thirdPartyComponents; } @end