Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,20 @@ const {StructCollector} = require('./StructCollector');

type FilesOutput = Map<string, string>;

const SwiftCompatibleDeclarationTemplate = ({
hasteModuleName,
protocolMethods,
}: $ReadOnly<{
hasteModuleName: string,
protocolMethods: string,
}>) => `
@protocol ${hasteModuleName}Spec <RCTBridgeModule, RCTSwiftTurboModule>

${protocolMethods}

@end
`

const ModuleDeclarationTemplate = ({
hasteModuleName,
structDeclarations,
Expand Down Expand Up @@ -58,6 +72,51 @@ namespace facebook::react {
};
} // namespace facebook::react`;

const SwiftCompatibleHeaderFileTemplate = ({
headerFileName,
moduleDeclarations,
assumeNonnull,
}: $ReadOnly<{
headerFileName: string,
moduleDeclarations: string,
assumeNonnull: boolean,
}>) => {
const headerFileNameWithNoExt = headerFileName
.replace(/\.h$/, '')
.replace(/-/, '');

return (
`/**
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
*
* Do not edit this file as changes may cause incorrect behavior and will be lost
* once the code is regenerated.
*
* ${'@'}generated by codegen project: GenerateModuleObjCpp
*
* We create an umbrella header (and corresponding implementation) here since
* Cxx compilation in BUCK has a limitation: source-code producing genrule()s
* must have a single output. More files => more genrule()s => slower builds.
*/

// Avoid multiple includes of ${headerFileNameWithNoExt} symbols
#ifndef ${headerFileNameWithNoExt}_H
#define ${headerFileNameWithNoExt}_H

#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import <ReactCommon/RCTSwiftTurboModule.h>

` +
(assumeNonnull ? '\nNS_ASSUME_NONNULL_BEGIN\n' : '') +
moduleDeclarations +
'\n' +
(assumeNonnull ? '\nNS_ASSUME_NONNULL_END\n' : '\n') +
`#endif // ${headerFileNameWithNoExt}_H` +
'\n'
);
};

const HeaderFileTemplate = ({
headerFileName,
moduleDeclarations,
Expand Down Expand Up @@ -150,6 +209,7 @@ module.exports = {
const nativeModules = getModules(schema);

const moduleDeclarations: Array<string> = [];
const swiftCompatibleModulesDeclarations: Array<string> = [];
const structInlineMethods: Array<string> = [];
const moduleImplementations: Array<string> = [];

Expand Down Expand Up @@ -209,6 +269,15 @@ module.exports = {
}),
);

swiftCompatibleModulesDeclarations.push(
SwiftCompatibleDeclarationTemplate({
hasteModuleName: hasteModuleName,
protocolMethods: methodSerializations
.map(({protocolMethod}) => protocolMethod)
.join('\n'),
}),
);

structInlineMethods.push(methodStrs.join('\n'));

moduleImplementations.push(
Expand All @@ -232,6 +301,13 @@ module.exports = {
assumeNonnull,
});

const swiftCompatibleHeaderFileName = `${libraryName}-Swift.h`;
const swiftHeaderFile = SwiftCompatibleHeaderFileTemplate({
headerFileName: swiftCompatibleHeaderFileName,
moduleDeclarations: swiftCompatibleModulesDeclarations.join('\n'),
assumeNonnull,
});

const sourceFileName = `${libraryName}-generated.mm`;
const sourceFile = SourceFileTemplate({
headerFileName,
Expand All @@ -240,6 +316,7 @@ module.exports = {

return new Map([
[headerFileName, headerFile],
[swiftCompatibleHeaderFileName, swiftHeaderFile],
[sourceFileName, sourceFile],
]);
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//
// RCTModule.mm
// Pods
//
// Created by Riccardo Cipolleschi on 18/07/2025.
//

@protocol RCTModule

@end

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// RCTSwiftTurboModule.h
// Pods
//
// Created by Riccardo Cipolleschi on 18/07/2025.
//

#import "RCTModule.h"

@protocol RCTSwiftTurboModule <RCTModule>

@end

Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
#import <string>
#import <unordered_map>

#import "RCTModule.h"

#define RCT_IS_TURBO_MODULE_CLASS(klass) \
((RCTTurboModuleEnabled() && [(klass) conformsToProtocol:@protocol(RCTTurboModule)]))
((RCTTurboModuleEnabled() && [(klass) conformsToProtocol:@protocol(RCTModule)]))
#define RCT_IS_TURBO_MODULE_INSTANCE(module) RCT_IS_TURBO_MODULE_CLASS([(module) class])

namespace facebook::react {
Expand Down Expand Up @@ -182,13 +184,20 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule {
*/
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
(const facebook::react::ObjCTurboModule::InitParams &)params;

@optional
/**
* Return an instance of an Apple Module
*/
- (Class<RCTModule>)getAppleModule;

@end

/**
* Protocol that objects can inherit to conform to be treated as turbomodules.
* It inherits from RCTTurboModuleProvider, meaning that a TurboModule can create itself
*/
@protocol RCTTurboModule <RCTModuleProvider>
@protocol RCTTurboModule <RCTModule, RCTModuleProvider>

@optional
- (void)setEventEmitterCallback:(EventEmitterCallbackWrapper *)eventEmitterCallbackWrapper;
Expand All @@ -206,3 +215,4 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule {
- (std::shared_ptr<facebook::react::NativeMethodCallInvoker>)decorateNativeMethodCallInvoker:
(std::shared_ptr<facebook::react::NativeMethodCallInvoker>)nativeMethodCallInvoker;
@end

Original file line number Diff line number Diff line change
Expand Up @@ -865,7 +865,7 @@ TraceSection s(

void ObjCTurboModule::setEventEmitterCallback(EventEmitterCallback eventEmitterCallback)
{
if ([instance_ conformsToProtocol:@protocol(RCTTurboModule)] &&
if ([instance_ conformsToProtocol:@protocol(RCTModule)] &&
[instance_ respondsToSelector:@selector(setEventEmitterCallback:)]) {
EventEmitterCallbackWrapper *wrapper = [EventEmitterCallbackWrapper new];
wrapper->_eventEmitterCallback = std::move(eventEmitterCallback);
Expand All @@ -877,3 +877,4 @@ TraceSection s(

@implementation EventEmitterCallbackWrapper
@end

Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ void invokeSync(const std::string &methodName, std::function<void()> &&work) ove

bool isTurboModuleClass(Class cls)
{
return [cls conformsToProtocol:@protocol(RCTTurboModule)];
return [cls conformsToProtocol:@protocol(RCTModule)];
}

bool isTurboModuleInstance(id module)
Expand Down Expand Up @@ -356,18 +356,24 @@ - (instancetype)initWithBridgeProxy:(RCTBridgeProxy *)bridgeProxy
/**
* Step 2: Look for platform-specific modules.
*/
id<RCTModuleProvider> module = [self _moduleProviderForName:moduleName];
id<RCTModuleProvider> moduleProvider = [self _moduleProviderForName:moduleName];

TurboModulePerfLogger::moduleJSRequireEndingStart(moduleName);

// If we request that a TurboModule be created, its respective ObjC class must exist
// If the class doesn't exist, then _provideObjCModule returns nil
if (!module) {
if (!moduleProvider) {
return nullptr;
}

id<RCTModuleProvider> module = nullptr;
if ([moduleProvider respondsToSelector:@selector(getAppleModule)]) {
module = (id<RCTModuleProvider>)[self _provideObjCModule:moduleName moduleProvider:moduleProvider];
}
id<RCTModuleProvider> moduleOrProvider = module ? module : moduleProvider;

std::shared_ptr<NativeMethodCallInvoker> nativeMethodCallInvoker = nullptr;
dispatch_queue_t methodQueue = (dispatch_queue_t)objc_getAssociatedObject(module, &kAssociatedMethodQueueKey);
dispatch_queue_t methodQueue = (dispatch_queue_t)objc_getAssociatedObject(moduleOrProvider, &kAssociatedMethodQueueKey);
if (methodQueue) {
/**
* Step 2c: Create and native CallInvoker from the TurboModule's method queue.
Expand All @@ -384,32 +390,44 @@ - (instancetype)initWithBridgeProxy:(RCTBridgeProxy *)bridgeProxy
}

/**
* Step 3: Return an exact sub-class of ObjC TurboModule
* Step 2d: If the moduleClass is a legacy CxxModule, return a TurboCxxModule instance that
* wraps CxxModule.
*/
Class moduleClass = [moduleOrProvider class];
// if ([moduleClass isSubclassOfClass:RCTCxxModule.class]) {
// // Use TurboCxxModule compat class to wrap the CxxModule instance.
// // This is only for migration convenience, despite less performant.
// auto turboModule = std::make_shared<TurboCxxModule>([((RCTCxxModule *)moduleOrProvider) createModule], _jsInvoker);
// _turboModuleCache.insert({moduleName, turboModule});
// return turboModule;
// }

/**
* Step 2e: Return an exact sub-class of ObjC TurboModule
*
* Use respondsToSelector: below to infer conformance to @protocol(RCTTurboModule). Using conformsToProtocol: is
* expensive.
*/
Class moduleClass = [module class];
if ([module respondsToSelector:@selector(getTurboModule:)]) {
if ([moduleProvider respondsToSelector:@selector(getTurboModule:)]) {
ObjCTurboModule::InitParams params = {
.moduleName = moduleName,
.instance = (id<RCTBridgeModule>)module,
.instance = (id<RCTBridgeModule>)moduleOrProvider,
.jsInvoker = _jsInvoker,
.nativeMethodCallInvoker = nativeMethodCallInvoker,
.isSyncModule = methodQueue == RCTJSThread,
};

auto turboModule = [(id<RCTTurboModule>)module getTurboModule:params];
auto turboModule = [(id<RCTTurboModule>)moduleProvider getTurboModule:params];
if (turboModule == nullptr) {
RCTLogError(@"TurboModule \"%@\"'s getTurboModule: method returned nil.", moduleClass);
}
_turboModuleCache.insert({moduleName, turboModule});

if ([module respondsToSelector:@selector(installJSIBindingsWithRuntime:callInvoker:)]) {
[(id<RCTTurboModuleWithJSIBindings>)module installJSIBindingsWithRuntime:*runtime callInvoker:_jsInvoker];
} else if ([module respondsToSelector:@selector(installJSIBindingsWithRuntime:)]) {
if ([moduleOrProvider respondsToSelector:@selector(installJSIBindingsWithRuntime:callInvoker:)]) {
[(id<RCTTurboModuleWithJSIBindings>)moduleOrProvider installJSIBindingsWithRuntime:*runtime callInvoker:_jsInvoker];
} else if ([moduleOrProvider respondsToSelector:@selector(installJSIBindingsWithRuntime:)]) {
// Old API without CallInvoker (deprecated)
[(id<RCTTurboModuleWithJSIBindings>)module installJSIBindingsWithRuntime:*runtime];
[(id<RCTTurboModuleWithJSIBindings>)moduleOrProvider installJSIBindingsWithRuntime:*runtime];
}
return turboModule;
}
Expand Down Expand Up @@ -490,16 +508,16 @@ - (BOOL)_isLegacyModuleClass:(Class)moduleClass
if ([_delegate respondsToSelector:@selector(getModuleProvider:)]) {
moduleProvider = [_delegate getModuleProvider:moduleName];
}

if (RCTTurboModuleInteropEnabled() && ![self _isTurboModule:moduleName] && !moduleProvider) {
return nil;
}

if (moduleProvider) {
if ([moduleProvider conformsToProtocol:@protocol(RCTTurboModule)]) {
// moduleProvider is also a TM, we need to initialize objectiveC properties, like the dispatch queue
if ([moduleProvider conformsToProtocol:@protocol(RCTModule)]) {
return (id<RCTModuleProvider>)[self _provideObjCModule:moduleName moduleProvider:moduleProvider];
}

// module is Cxx module
return moduleProvider;
}
Expand Down Expand Up @@ -579,7 +597,12 @@ - (ModuleHolder *)_getOrCreateModuleHolder:(const char *)moduleName
/**
* Step 2a: Resolve platform-specific class.
*/
Class moduleClass = moduleProvider ? [moduleProvider class] : [self _getModuleClassFromName:moduleName];
Class moduleClass = moduleProvider ?
([moduleProvider respondsToSelector:@selector(getAppleModule)] ?
[moduleProvider getAppleModule] :
[moduleProvider class]) :
[self _getModuleClassFromName:moduleName];


__block id<RCTBridgeModule> module = nil;

Expand Down Expand Up @@ -634,7 +657,7 @@ - (BOOL)_shouldCreateObjCModule:(Class)moduleClass
return [moduleClass conformsToProtocol:@protocol(RCTBridgeModule)];
}

return [moduleClass conformsToProtocol:@protocol(RCTTurboModule)];
return [moduleClass conformsToProtocol:@protocol(RCTModule)];
}

/**
Expand Down Expand Up @@ -1105,3 +1128,4 @@ - (void)_invalidateModules
}

@end

Loading
Loading