diff --git a/packages/react-native/React/Base/RCTBridge.mm b/packages/react-native/React/Base/RCTBridge.mm index d7379488d371..6f141963feff 100644 --- a/packages/react-native/React/Base/RCTBridge.mm +++ b/packages/react-native/React/Base/RCTBridge.mm @@ -177,7 +177,7 @@ void RCTRegisterModule(Class moduleClass) cls); #endif - NSString *name = [cls moduleName]; + NSString *name = [cls respondsToSelector:@selector(moduleName)] ? [cls moduleName] : nil; if (name.length == 0) { name = NSStringFromClass(cls); } diff --git a/packages/react-native/React/Base/RCTBridgeModule.h b/packages/react-native/React/Base/RCTBridgeModule.h index 6f2e8115f79f..14c479b25bd0 100644 --- a/packages/react-native/React/Base/RCTBridgeModule.h +++ b/packages/react-native/React/Base/RCTBridgeModule.h @@ -122,11 +122,12 @@ RCT_EXTERN_C_END #endif // defined(RCT_REMOVE_LEGACY_MODULE_INTEROP) && defined(RCT_REMOVE_LEGACY_COMPONENT_INTEROP) -// Implemented by RCT_EXPORT_MODULE -+ (NSString *)moduleName; - @optional +// Implemented by RCT_EXPORT_MODULE. When absent, callers fall back to the ObjC class +// name, so a TurboModule resolved via a provider or plugin socket need not declare it. ++ (NSString *)moduleName; + /** * A reference to the RCTModuleRegistry. Useful for modules that require access * to other NativeModules. To implement this in your module, just add `@synthesize diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropComponentView.mm index cc081964b1f3..e0808af76e4f 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropComponentView.mm @@ -130,9 +130,10 @@ + (BOOL)isSupported:(NSString *)componentName for (Class moduleClass in registeredModules) { id bridgeModule = (id)moduleClass; - NSString *moduleName = [[bridgeModule moduleName] isEqualToString:@""] + NSString *exportedName = [bridgeModule respondsToSelector:@selector(moduleName)] ? [bridgeModule moduleName] : nil; + NSString *moduleName = exportedName.length == 0 ? [NSStringFromClass(moduleClass) stringByReplacingOccurrencesOfString:@"Manager" withString:@""] - : [bridgeModule moduleName]; + : exportedName; if (supportedLegacyViewComponents[moduleName] == NULL) { supportedLegacyViewComponents[moduleName] = moduleClass; diff --git a/packages/react-native/React/Views/RCTComponentData.mm b/packages/react-native/React/Views/RCTComponentData.mm index 64190118a881..6004e4e7c646 100644 --- a/packages/react-native/React/Views/RCTComponentData.mm +++ b/packages/react-native/React/Views/RCTComponentData.mm @@ -544,7 +544,7 @@ - (void)setProps:(NSDictionary *)props forView:(id // We want to get rid of RCT and RK prefixes, but a lot of JS code still references // view names by prefix. So, while RCTBridgeModuleNameForClass now drops these // prefixes by default, we'll still keep them around here. - NSString *name = [managerClass moduleName]; + NSString *name = [managerClass respondsToSelector:@selector(moduleName)] ? [managerClass moduleName] : nil; if (name.length == 0) { name = NSStringFromClass(managerClass); } diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm index c5368ea8842f..10f87ec53f89 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm @@ -763,6 +763,10 @@ - (Class)_getModuleClassFromName:(const char *)moduleName NSString *objcModuleName = [NSString stringWithUTF8String:moduleName]; NSArray *modules = RCTGetModuleClasses(); for (Class current in modules) { + // A class without +moduleName has no custom JS name, so it can never match here. + if (![current respondsToSelector:@selector(moduleName)]) { + continue; + } NSString *currentModuleName = [current moduleName]; if ([objcModuleName isEqualToString:currentModuleName]) { return current;