Skip to content

Commit c3c0c7c

Browse files
committed
[RN][iOS]Add basic support for TM in Swift - Codegen Disabled
1 parent 5cda306 commit c3c0c7c

7 files changed

Lines changed: 71 additions & 24 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//
2+
// RCTModule.mm
3+
// Pods
4+
//
5+
// Created by Riccardo Cipolleschi on 18/07/2025.
6+
//
7+
8+
@protocol RCTModule
9+
10+
@end
11+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//
2+
// RCTSwiftTurboModule.h
3+
// Pods
4+
//
5+
// Created by Riccardo Cipolleschi on 18/07/2025.
6+
//
7+
8+
#import "RCTModule.h"
9+
10+
@protocol RCTSwiftTurboModule <RCTModule>
11+
12+
@end
13+

packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919
#import <string>
2020
#import <unordered_map>
2121

22+
#import "RCTModule.h"
23+
2224
#define RCT_IS_TURBO_MODULE_CLASS(klass) \
23-
((RCTTurboModuleEnabled() && [(klass) conformsToProtocol:@protocol(RCTTurboModule)]))
25+
((RCTTurboModuleEnabled() && [(klass) conformsToProtocol:@protocol(RCTModule)]))
2426
#define RCT_IS_TURBO_MODULE_INSTANCE(module) RCT_IS_TURBO_MODULE_CLASS([(module) class])
2527

2628
namespace facebook::react {
@@ -186,13 +188,20 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule {
186188
*/
187189
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
188190
(const facebook::react::ObjCTurboModule::InitParams &)params;
191+
192+
@optional
193+
/**
194+
* Return an instance of an Apple Module
195+
*/
196+
- (Class<RCTModule>)getAppleModule;
197+
189198
@end
190199

191200
/**
192201
* Protocol that objects can inherit to conform to be treated as turbomodules.
193202
* It inherits from RCTTurboModuleProvider, meaning that a TurboModule can create itself
194203
*/
195-
@protocol RCTTurboModule <RCTModuleProvider>
204+
@protocol RCTTurboModule <RCTModule, RCTModuleProvider>
196205

197206
@optional
198207
- (void)setEventEmitterCallback:(EventEmitterCallbackWrapper *)eventEmitterCallbackWrapper;
@@ -210,3 +219,4 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule {
210219
- (std::shared_ptr<facebook::react::NativeMethodCallInvoker>)decorateNativeMethodCallInvoker:
211220
(std::shared_ptr<facebook::react::NativeMethodCallInvoker>)nativeMethodCallInvoker;
212221
@end
222+

packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -847,7 +847,7 @@ TraceSection s(
847847

848848
void ObjCTurboModule::setEventEmitterCallback(EventEmitterCallback eventEmitterCallback)
849849
{
850-
if ([instance_ conformsToProtocol:@protocol(RCTTurboModule)] &&
850+
if ([instance_ conformsToProtocol:@protocol(RCTModule)] &&
851851
[instance_ respondsToSelector:@selector(setEventEmitterCallback:)]) {
852852
EventEmitterCallbackWrapper *wrapper = [EventEmitterCallbackWrapper new];
853853
wrapper->_eventEmitterCallback = std::move(eventEmitterCallback);
@@ -859,3 +859,4 @@ TraceSection s(
859859

860860
@implementation EventEmitterCallbackWrapper
861861
@end
862+

packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ void invokeSync(const std::string &methodName, std::function<void()> &&work) ove
159159

160160
bool isTurboModuleClass(Class cls)
161161
{
162-
return [cls conformsToProtocol:@protocol(RCTTurboModule)];
162+
return [cls conformsToProtocol:@protocol(RCTModule)];
163163
}
164164

165165
bool isTurboModuleInstance(id module)
@@ -339,18 +339,24 @@ - (instancetype)initWithBridgeProxy:(RCTBridgeProxy *)bridgeProxy
339339
/**
340340
* Step 2: Look for platform-specific modules.
341341
*/
342-
id<RCTModuleProvider> module = [self _moduleProviderForName:moduleName];
342+
id<RCTModuleProvider> moduleProvider = [self _moduleProviderForName:moduleName];
343343

344344
TurboModulePerfLogger::moduleJSRequireEndingStart(moduleName);
345345

346346
// If we request that a TurboModule be created, its respective ObjC class must exist
347347
// If the class doesn't exist, then _provideObjCModule returns nil
348-
if (!module) {
348+
if (!moduleProvider) {
349349
return nullptr;
350350
}
351+
352+
id<RCTModuleProvider> module = nullptr;
353+
if ([moduleProvider respondsToSelector:@selector(getAppleModule)]) {
354+
module = (id<RCTModuleProvider>)[self _provideObjCModule:moduleName moduleProvider:moduleProvider];
355+
}
356+
id<RCTModuleProvider> moduleOrProvider = module ? module : moduleProvider;
351357

352358
std::shared_ptr<NativeMethodCallInvoker> nativeMethodCallInvoker = nullptr;
353-
dispatch_queue_t methodQueue = (dispatch_queue_t)objc_getAssociatedObject(module, &kAssociatedMethodQueueKey);
359+
dispatch_queue_t methodQueue = (dispatch_queue_t)objc_getAssociatedObject(moduleOrProvider, &kAssociatedMethodQueueKey);
354360
if (methodQueue) {
355361
/**
356362
* Step 2c: Create and native CallInvoker from the TurboModule's method queue.
@@ -370,11 +376,11 @@ - (instancetype)initWithBridgeProxy:(RCTBridgeProxy *)bridgeProxy
370376
* Step 2d: If the moduleClass is a legacy CxxModule, return a TurboCxxModule instance that
371377
* wraps CxxModule.
372378
*/
373-
Class moduleClass = [module class];
379+
Class moduleClass = [moduleOrProvider class];
374380
if ([moduleClass isSubclassOfClass:RCTCxxModule.class]) {
375381
// Use TurboCxxModule compat class to wrap the CxxModule instance.
376382
// This is only for migration convenience, despite less performant.
377-
auto turboModule = std::make_shared<TurboCxxModule>([((RCTCxxModule *)module) createModule], _jsInvoker);
383+
auto turboModule = std::make_shared<TurboCxxModule>([((RCTCxxModule *)moduleOrProvider) createModule], _jsInvoker);
378384
_turboModuleCache.insert({moduleName, turboModule});
379385
return turboModule;
380386
}
@@ -385,27 +391,27 @@ - (instancetype)initWithBridgeProxy:(RCTBridgeProxy *)bridgeProxy
385391
* Use respondsToSelector: below to infer conformance to @protocol(RCTTurboModule). Using conformsToProtocol: is
386392
* expensive.
387393
*/
388-
if ([module respondsToSelector:@selector(getTurboModule:)]) {
394+
if ([moduleProvider respondsToSelector:@selector(getTurboModule:)]) {
389395
ObjCTurboModule::InitParams params = {
390396
.moduleName = moduleName,
391-
.instance = (id<RCTBridgeModule>)module,
397+
.instance = (id<RCTBridgeModule>)moduleOrProvider,
392398
.jsInvoker = _jsInvoker,
393399
.nativeMethodCallInvoker = nativeMethodCallInvoker,
394400
.isSyncModule = methodQueue == RCTJSThread,
395401
.shouldVoidMethodsExecuteSync = (bool)RCTTurboModuleSyncVoidMethodsEnabled(),
396402
};
397403

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

404-
if ([module respondsToSelector:@selector(installJSIBindingsWithRuntime:callInvoker:)]) {
405-
[(id<RCTTurboModuleWithJSIBindings>)module installJSIBindingsWithRuntime:*runtime callInvoker:_jsInvoker];
406-
} else if ([module respondsToSelector:@selector(installJSIBindingsWithRuntime:)]) {
410+
if ([moduleOrProvider respondsToSelector:@selector(installJSIBindingsWithRuntime:callInvoker:)]) {
411+
[(id<RCTTurboModuleWithJSIBindings>)moduleOrProvider installJSIBindingsWithRuntime:*runtime callInvoker:_jsInvoker];
412+
} else if ([moduleOrProvider respondsToSelector:@selector(installJSIBindingsWithRuntime:)]) {
407413
// Old API without CallInvoker (deprecated)
408-
[(id<RCTTurboModuleWithJSIBindings>)module installJSIBindingsWithRuntime:*runtime];
414+
[(id<RCTTurboModuleWithJSIBindings>)moduleOrProvider installJSIBindingsWithRuntime:*runtime];
409415
}
410416
return turboModule;
411417
}
@@ -496,16 +502,16 @@ - (BOOL)_isLegacyModuleClass:(Class)moduleClass
496502
if ([_delegate respondsToSelector:@selector(getModuleProvider:)]) {
497503
moduleProvider = [_delegate getModuleProvider:moduleName];
498504
}
499-
505+
500506
if (RCTTurboModuleInteropEnabled() && ![self _isTurboModule:moduleName] && !moduleProvider) {
501507
return nil;
502508
}
503-
509+
504510
if (moduleProvider) {
505-
if ([moduleProvider conformsToProtocol:@protocol(RCTTurboModule)]) {
506-
// moduleProvider is also a TM, we need to initialize objectiveC properties, like the dispatch queue
511+
if ([moduleProvider conformsToProtocol:@protocol(RCTModule)]) {
507512
return (id<RCTModuleProvider>)[self _provideObjCModule:moduleName moduleProvider:moduleProvider];
508513
}
514+
509515
// module is Cxx module
510516
return moduleProvider;
511517
}
@@ -585,7 +591,12 @@ - (ModuleHolder *)_getOrCreateModuleHolder:(const char *)moduleName
585591
/**
586592
* Step 2a: Resolve platform-specific class.
587593
*/
588-
Class moduleClass = moduleProvider ? [moduleProvider class] : [self _getModuleClassFromName:moduleName];
594+
Class moduleClass = moduleProvider ?
595+
([moduleProvider respondsToSelector:@selector(getAppleModule)] ?
596+
[moduleProvider getAppleModule] :
597+
[moduleProvider class]) :
598+
[self _getModuleClassFromName:moduleName];
599+
589600

590601
__block id<RCTBridgeModule> module = nil;
591602

@@ -640,7 +651,7 @@ - (BOOL)_shouldCreateObjCModule:(Class)moduleClass
640651
return [moduleClass conformsToProtocol:@protocol(RCTBridgeModule)];
641652
}
642653

643-
return [moduleClass conformsToProtocol:@protocol(RCTTurboModule)];
654+
return [moduleClass conformsToProtocol:@protocol(RCTModule)];
644655
}
645656

646657
/**
@@ -1104,3 +1115,4 @@ - (void)_invalidateModules
11041115
}
11051116

11061117
@end
1118+

packages/react-native/scripts/codegen/templates/ReactAppDependencyProvider.podspec.template

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Pod::Spec.new do |s|
2222
s.author = "Meta Platforms, Inc. and its affiliates"
2323
s.platforms = min_supported_versions
2424
s.source = source
25-
s.source_files = "**/RCTAppDependencyProvider.{h,mm}"
25+
s.source_files = ["**/RCTAppDependencyProvider.{h,mm}", "**/*-Swift.{h,mm}"]
2626

2727
# This guard prevent to install the dependencies when we run `pod install` in the old architecture.
2828
s.pod_target_xcconfig = {

private/helloworld/scripts/metro.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ program
2323
android: {},
2424
},
2525
root: path.join(__dirname, '../'),
26-
reactNativePath: path.join(__dirname, '../../react-native'),
26+
reactNativePath: path.join(__dirname, '../../../packages/react-native'),
2727
},
2828
{
2929
experimentalDebugger: false,

0 commit comments

Comments
 (0)