diff --git a/packages/react-native/Libraries/Image/RCTImageLoader.h b/packages/react-native/Libraries/Image/RCTImageLoader.h index 592785573066db..ae9dc8069743e3 100644 --- a/packages/react-native/Libraries/Image/RCTImageLoader.h +++ b/packages/react-native/Libraries/Image/RCTImageLoader.h @@ -8,6 +8,7 @@ #import #import +#import #import #import #import @@ -34,3 +35,9 @@ @property (nonatomic, readonly) RCTImageLoader *imageLoader; @end + +@interface RCTBridgeProxy (RCTImageLoader) + +@property (nonatomic, readonly) RCTImageLoader *imageLoader; + +@end diff --git a/packages/react-native/Libraries/Image/RCTImageLoader.mm b/packages/react-native/Libraries/Image/RCTImageLoader.mm index bfcdf26a5d3cee..09317d99c2d653 100644 --- a/packages/react-native/Libraries/Image/RCTImageLoader.mm +++ b/packages/react-native/Libraries/Image/RCTImageLoader.mm @@ -1293,6 +1293,15 @@ - (RCTImageLoader *)imageLoader @end +@implementation RCTBridgeProxy (RCTImageLoader) + +- (RCTImageLoader *)imageLoader +{ + return [self moduleForClass:[RCTImageLoader class]]; +} + +@end + Class RCTImageLoaderCls(void) { return RCTImageLoader.class; diff --git a/packages/react-native/Libraries/Image/RCTImageStoreManager.h b/packages/react-native/Libraries/Image/RCTImageStoreManager.h index 389f613ea8afe1..61f324d51cd4e8 100644 --- a/packages/react-native/Libraries/Image/RCTImageStoreManager.h +++ b/packages/react-native/Libraries/Image/RCTImageStoreManager.h @@ -8,6 +8,7 @@ #import #import +#import #import @interface RCTImageStoreManager : NSObject @@ -44,3 +45,9 @@ @property (nonatomic, readonly) RCTImageStoreManager *imageStoreManager; @end + +@interface RCTBridgeProxy (RCTImageStoreManager) + +@property (nonatomic, readonly) RCTImageStoreManager *imageStoreManager; + +@end diff --git a/packages/react-native/Libraries/Image/RCTImageStoreManager.mm b/packages/react-native/Libraries/Image/RCTImageStoreManager.mm index acfa4d3434c4eb..242c42cce8a0e5 100644 --- a/packages/react-native/Libraries/Image/RCTImageStoreManager.mm +++ b/packages/react-native/Libraries/Image/RCTImageStoreManager.mm @@ -254,6 +254,15 @@ - (RCTImageStoreManager *)imageStoreManager @end +@implementation RCTBridgeProxy (RCTImageStoreManager) + +- (RCTImageStoreManager *)imageStoreManager +{ + return [self moduleForClass:[RCTImageStoreManager class]]; +} + +@end + Class RCTImageStoreManagerCls(void) { return RCTImageStoreManager.class; diff --git a/packages/react-native/Libraries/Network/RCTNetworking.h b/packages/react-native/Libraries/Network/RCTNetworking.h index 3ad70f0ce80fd5..fcc9fe81714a29 100644 --- a/packages/react-native/Libraries/Network/RCTNetworking.h +++ b/packages/react-native/Libraries/Network/RCTNetworking.h @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. */ +#import #import #import #import @@ -61,6 +62,12 @@ @end +@interface RCTBridgeProxy (RCTNetworking) + +@property (nonatomic, readonly) RCTNetworking *networking; + +@end + // HACK: When uploading images/video from PHAssetLibrary, we change the URL scheme to be // ph-upload://. This is to ensure that we upload a full video when given a ph-upload:// URL, // instead of just the thumbnail. Consider the following problem: diff --git a/packages/react-native/Libraries/Network/RCTNetworking.mm b/packages/react-native/Libraries/Network/RCTNetworking.mm index 9b9c12711dea7c..4e78d8e798e8ae 100644 --- a/packages/react-native/Libraries/Network/RCTNetworking.mm +++ b/packages/react-native/Libraries/Network/RCTNetworking.mm @@ -762,6 +762,15 @@ - (RCTNetworking *)networking @end +@implementation RCTBridgeProxy (RCTNetworking) + +- (RCTNetworking *)networking +{ + return [self moduleForClass:[RCTNetworking class]]; +} + +@end + Class RCTNetworkingCls(void) { return RCTNetworking.class; diff --git a/packages/react-native/React/Base/RCTBridgeProxy.h b/packages/react-native/React/Base/RCTBridgeProxy.h new file mode 100644 index 00000000000000..b1c70287b1cc35 --- /dev/null +++ b/packages/react-native/React/Base/RCTBridgeProxy.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import + +#import "RCTBridgeModule.h" + +@class RCTBundleManager; +@class RCTCallableJSModules; +@class RCTModuleRegistry; +@class RCTViewRegistry; + +@interface RCTBridgeProxy : NSProxy +- (instancetype)initWithViewRegistry:(RCTViewRegistry *)viewRegistry + moduleRegistry:(RCTModuleRegistry *)moduleRegistry + bundleManager:(RCTBundleManager *)bundleManager + callableJSModules:(RCTCallableJSModules *)callableJSModules + dispatchToJSThread:(void (^)(dispatch_block_t))dispatchToJSThread NS_DESIGNATED_INITIALIZER; + +- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel; +- (void)forwardInvocation:(NSInvocation *)invocation; + +- (void)logWarning:(NSString *)message cmd:(SEL)cmd; +- (void)logError:(NSString *)message cmd:(SEL)cmd; + +/** + * Methods required for RCTBridge class extensions + */ +- (id)moduleForClass:(Class)moduleClass; +- (id)moduleForName:(NSString *)moduleName lazilyLoadIfNecessary:(BOOL)lazilyLoad; +@end diff --git a/packages/react-native/React/Base/RCTBridgeProxy.mm b/packages/react-native/React/Base/RCTBridgeProxy.mm new file mode 100644 index 00000000000000..c91a0fe9667400 --- /dev/null +++ b/packages/react-native/React/Base/RCTBridgeProxy.mm @@ -0,0 +1,447 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "RCTBridgeProxy.h" +#import +#import +#import +#import +#import + +using namespace facebook; + +@interface RCTUIManagerProxy : NSProxy +- (instancetype)initWithViewRegistry:(RCTViewRegistry *)viewRegistry NS_DESIGNATED_INITIALIZER; + +- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel; +- (void)forwardInvocation:(NSInvocation *)invocation; +@end + +@implementation RCTBridgeProxy { + RCTUIManagerProxy *_uiManagerProxy; + RCTModuleRegistry *_moduleRegistry; + RCTBundleManager *_bundleManager; + RCTCallableJSModules *_callableJSModules; + void (^_dispatchToJSThread)(dispatch_block_t); +} + +- (instancetype)initWithViewRegistry:(RCTViewRegistry *)viewRegistry + moduleRegistry:(RCTModuleRegistry *)moduleRegistry + bundleManager:(RCTBundleManager *)bundleManager + callableJSModules:(RCTCallableJSModules *)callableJSModules + dispatchToJSThread:(void (^)(dispatch_block_t))dispatchToJSThread +{ + self = [super self]; + if (self) { + self->_uiManagerProxy = [[RCTUIManagerProxy alloc] initWithViewRegistry:viewRegistry]; + self->_moduleRegistry = moduleRegistry; + self->_bundleManager = bundleManager; + self->_callableJSModules = callableJSModules; + self->_dispatchToJSThread = dispatchToJSThread; + } + return self; +} + +- (void)dispatchBlock:(dispatch_block_t)block queue:(dispatch_queue_t)queue +{ + [self logWarning:@"Please migrate to dispatchToJSThread: @synthesize dispatchToJSThread = _dispatchToJSThread" + cmd:_cmd]; + + if (queue == RCTJSThread) { + _dispatchToJSThread(block); + } else if (queue) { + dispatch_async(queue, block); + } +} + +/** + * Used By: + * - RCTDevSettings + */ +- (Class)executorClass +{ + [self logWarning:@"This method is unsupported. Returning nil." cmd:_cmd]; + return nil; +} + +/** + * Used By: + * - RCTBlobCollector + */ +- (jsi::Runtime *)runtime +{ + [self logWarning:@"This method is unsupported. Returning nullptr." cmd:_cmd]; + return nullptr; +} + +/** + * RCTModuleRegistry + */ +- (id)moduleForName:(NSString *)moduleName +{ + [self logWarning:@"Please migrate to RCTModuleRegistry: @synthesize moduleRegistry = _moduleRegistry." cmd:_cmd]; + return [_moduleRegistry moduleForName:[moduleName UTF8String]]; +} + +- (id)moduleForName:(NSString *)moduleName lazilyLoadIfNecessary:(BOOL)lazilyLoad +{ + [self logWarning:@"Please migrate to RCTModuleRegistry: @synthesize moduleRegistry = _moduleRegistry." cmd:_cmd]; + return [_moduleRegistry moduleForName:[moduleName UTF8String] lazilyLoadIfNecessary:lazilyLoad]; +} + +- (id)moduleForClass:(Class)moduleClass +{ + [self logWarning:@"Please migrate to RCTModuleRegistry: @synthesize moduleRegistry = _moduleRegistry." cmd:_cmd]; + NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass); + return [_moduleRegistry moduleForName:[moduleName UTF8String] lazilyLoadIfNecessary:YES]; +} + +- (NSArray *)modulesConformingToProtocol:(Protocol *)protocol +{ + [self logError:@"The TurboModule system cannot load modules by protocol. Returning an empty NSArray*." cmd:_cmd]; + return @[]; +} + +- (BOOL)moduleIsInitialized:(Class)moduleClass +{ + [self logWarning:@"Please migrate to RCTModuleRegistry: @synthesize moduleRegistry = _moduleRegistry." cmd:_cmd]; + return [_moduleRegistry moduleIsInitialized:moduleClass]; +} + +- (NSArray *)moduleClasses +{ + [self logError:@"The TurboModuleManager does not implement this method. Returning an empty NSArray*." cmd:_cmd]; + return @[]; +} + +/** + * RCTBundleManager + */ +- (void)setBundleURL:(NSURL *)bundleURL +{ + [self logWarning:@"Please migrate to RCTBundleManager: @synthesize bundleManager = _bundleManager." cmd:_cmd]; + [_bundleManager setBundleURL:bundleURL]; +} + +- (NSURL *)bundleURL +{ + [self logWarning:@"Please migrate to RCTBundleManager: @synthesize bundleManager = _bundleManager." cmd:_cmd]; + return [_bundleManager bundleURL]; +} + +/** + * RCTCallableJSModules + */ +- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args +{ + [self logWarning:@"Please migrate to RCTCallableJSModules: @synthesize callableJSModules = _callableJSModules." + cmd:_cmd]; + + NSArray *ids = [moduleDotMethod componentsSeparatedByString:@"."]; + NSString *module = ids[0]; + NSString *method = ids[1]; + [_callableJSModules invokeModule:module method:method withArgs:args]; +} + +- (void)enqueueJSCall:(NSString *)module + method:(NSString *)method + args:(NSArray *)args + completion:(dispatch_block_t)completion +{ + [self logWarning:@"Please migrate to RCTCallableJSModules: @synthesize callableJSModules = _callableJSModules." + cmd:_cmd]; + [_callableJSModules invokeModule:module method:method withArgs:args onComplete:completion]; +} + +- (void)registerSegmentWithId:(NSUInteger)segmentId path:(NSString *)path +{ + [self logError:@"Please migrate to RCTHost registerSegmentWithId. Nooping" cmd:_cmd]; +} + +- (id)delegate +{ + [self logError:@"This method is unsupported. Returning nil." cmd:_cmd]; + return nil; +} + +- (NSDictionary *)launchOptions +{ + [self logError:@"Bridgeless mode doesn't support launchOptions. Returning nil." cmd:_cmd]; + return nil; +} + +- (BOOL)loading +{ + [self logWarning:@"This method is not implemented. Returning NO." cmd:_cmd]; + return NO; +} + +- (BOOL)valid +{ + [self logWarning:@"This method is not implemented. Returning NO." cmd:_cmd]; + return NO; +} + +- (RCTPerformanceLogger *)performanceLogger +{ + [self logWarning:@"Bridgeless mode does not support RCTPerformanceLogger. Returning nil." cmd:_cmd]; + return nil; +} + +- (void)reload +{ + [self logError:@"Please use RCTReloadCommand instead. Nooping." cmd:_cmd]; +} + +- (void)reloadWithReason:(NSString *)reason +{ + [self logError:@"Please use RCTReloadCommand instead. Nooping." cmd:_cmd]; +} + +- (void)onFastRefresh +{ + [[NSNotificationCenter defaultCenter] postNotificationName:RCTBridgeFastRefreshNotification object:self]; +} + +- (void)requestReload __deprecated_msg("Use RCTReloadCommand instead") +{ + [self logError:@"Please use RCTReloadCommand instead. Nooping." cmd:_cmd]; +} + +- (BOOL)isBatchActive +{ + [self logWarning:@"Bridgeless mode does not support batching. Returning NO." cmd:_cmd]; + return NO; +} + +/** + * RCTBridge () + */ + +- (NSString *)bridgeDescription +{ + [self logWarning:@"Bridgeless mode does not support bridgeDescription. Returning \"BridgeProxy\"." cmd:_cmd]; + return @"BridgeProxy"; +} + +- (void)enqueueCallback:(NSNumber *)cbID args:(NSArray *)args +{ + [self logError:@"Bridgeless mode does not queuing callbacks by ids. No-oping." cmd:_cmd]; +} + +- (RCTBridge *)batchedBridge +{ + [self logWarning:@"Bridgeless mode does not support batchedBridge. Returning bridge proxy." cmd:_cmd]; + return (RCTBridge *)self; +} + +- (void)setBatchedBridge +{ + [self logError:@"Bridgeless mode does not support setBatchedBridge. No-oping." cmd:_cmd]; +} + +- (RCTBridgeModuleListProvider)moduleProvider +{ + [self logWarning:@"Bridgeless mode does not support RCTBridgeModuleListProvider. Returning empty block" cmd:_cmd]; + return ^{ + return @[]; + }; +} + +- (RCTModuleRegistry *)moduleRegistry +{ + return _moduleRegistry; +} + +/** + * RCTBridge (RCTCxxBridge) + */ + +- (RCTBridge *)parentBridge +{ + [self logWarning:@"Bridgeless mode does not support parentBridge. Returning bridge proxy." cmd:_cmd]; + return (RCTBridge *)self; +} + +- (BOOL)moduleSetupComplete +{ + [self logWarning:@"Bridgeless mode does not implement moduleSetupComplete. Returning YES." cmd:_cmd]; + return YES; +} + +- (void)start +{ + [self + logError: + @"Starting the bridge proxy does nothing. If you want to start React Native, please use RCTHost start. Nooping" + cmd:_cmd]; +} + +- (void)registerModuleForFrameUpdates:(id)module withModuleData:(RCTModuleData *)moduleData +{ + [self logError:@"Bridgeless mode does not allow custom modules to register themselves for frame updates. Nooping" + cmd:_cmd]; +} + +- (RCTModuleData *)moduleDataForName:(NSString *)moduleName +{ + [self logError:@"Bridgeless mode does not use RCTModuleData. Returning nil." cmd:_cmd]; + return nil; +} + +- (void)registerAdditionalModuleClasses:(NSArray *)newModules +{ + [self + logError: + @"This API is unsupported. Please return all module classes from your app's RCTTurboModuleManagerDelegate getModuleClassFromName:. Nooping." + cmd:_cmd]; +} + +- (void)updateModuleWithInstance:(id)instance +{ + [self logError:@"Bridgeless mode does not support module replacement. Nooping." cmd:_cmd]; +} + +- (void)startProfiling +{ + [self logWarning:@"Bridgeless mode does not support this method. Nooping." cmd:_cmd]; +} + +- (void)stopProfiling:(void (^)(NSData *))callback +{ + [self logWarning:@"Bridgeless mode does not support this method. Nooping." cmd:_cmd]; +} + +- (id)callNativeModule:(NSUInteger)moduleID method:(NSUInteger)methodID params:(NSArray *)params +{ + [self logError:@"Bridgeless mode does not support this method. Nooping and returning nil." cmd:_cmd]; + return nil; +} + +- (void)logMessage:(NSString *)message level:(NSString *)level +{ + [self logWarning:@"Bridgeless mode does not support this method. Nooping." cmd:_cmd]; +} + +- (void)_immediatelyCallTimer:(NSNumber *)timer +{ + [self logWarning:@"Bridgeless mode does not support this method. Nooping." cmd:_cmd]; +} + +/** + * RCTBridge (Inspector) + */ +- (BOOL)inspectable +{ + [self logWarning:@"Bridgeless mode does not support this method. Returning NO." cmd:_cmd]; + return NO; +} + +/** + * RCTBridge (RCTUIManager) + */ +- (RCTUIManager *)uiManager +{ + return (RCTUIManager *)_uiManagerProxy; +} + +/** + * NSProxy setup + */ +- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel; +{ + return [RCTCxxBridge instanceMethodSignatureForSelector:sel]; +} + +- (void)forwardInvocation:(NSInvocation *)invocation +{ + [self logError:@"This method is unsupported." cmd:invocation.selector]; +} + +/** + * Logging + * TODO(155977839): Add a means to configure/disable these logs, so people do not ignore all LogBoxes + */ +- (void)logWarning:(NSString *)message cmd:(SEL)cmd +{ + RCTLogWarn(@"RCTBridgeProxy: Calling [bridge %@]. %@", NSStringFromSelector(cmd), message); +} + +- (void)logError:(NSString *)message cmd:(SEL)cmd +{ + RCTLogError(@"RCTBridgeProxy: Calling [bridge %@]. %@", NSStringFromSelector(cmd), message); +} + +@end + +@implementation RCTUIManagerProxy { + RCTViewRegistry *_viewRegistry; +} +- (instancetype)initWithViewRegistry:(RCTViewRegistry *)viewRegistry +{ + self = [super self]; + if (self) { + _viewRegistry = viewRegistry; + } + return self; +} + +/** + * RCTViewRegistry + */ +- (UIView *)viewForReactTag:(NSNumber *)reactTag +{ + [self logWarning:@"Please migrate to RCTViewRegistry: @synthesize viewRegistry_DEPRECATED = _viewRegistry_DEPRECATED." + cmd:_cmd]; + return [_viewRegistry viewForReactTag:reactTag]; +} + +- (void)addUIBlock:(RCTViewManagerUIBlock)block +{ + [self + logWarning: + @"This method isn't implemented faithfully: the viewRegistry passed to RCTViewManagerUIBlock is nil. Please migrate to RCTViewRegistry: @synthesize viewRegistry_DEPRECATED = _viewRegistry_DEPRECATED." + cmd:_cmd]; + __weak __typeof(self) weakSelf = self; + RCTExecuteOnMainQueue(^{ + __typeof(self) strongSelf = weakSelf; + if (strongSelf) { + block((RCTUIManager *)strongSelf, nil); + } + }); +} + +/** + * NSProxy setup + */ +- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel +{ + return [RCTUIManager instanceMethodSignatureForSelector:sel]; +} + +- (void)forwardInvocation:(NSInvocation *)invocation +{ + [self logError:@"This methid is unsupported." cmd:invocation.selector]; +} + +/** + * Logging + * TODO(155977839): Add a means to configure/disable these logs, so people do not ignore all LogBoxes + */ +- (void)logWarning:(NSString *)message cmd:(SEL)cmd +{ + RCTLogWarn( + @"RCTBridgeProxy (RCTUIManagerProxy): Calling [bridge.uiManager %@]. %@", NSStringFromSelector(cmd), message); +} + +- (void)logError:(NSString *)message cmd:(SEL)cmd +{ + RCTLogError( + @"RCTBridgeProxy (RCTUIManagerProxy): Calling [bridge.uiManager %@]. %@", NSStringFromSelector(cmd), message); +} + +@end diff --git a/packages/react-native/React/Base/RCTEventDispatcher.m b/packages/react-native/React/Base/RCTEventDispatcher.m index 00f6046b4eb535..d779006e7e90b0 100644 --- a/packages/react-native/React/Base/RCTEventDispatcher.m +++ b/packages/react-native/React/Base/RCTEventDispatcher.m @@ -28,3 +28,12 @@ @implementation RCTBridge (RCTEventDispatcher) } @end + +@implementation RCTBridgeProxy (RCTEventDispatcher) + +- (id)eventDispatcher +{ + return [self moduleForName:@"EventDispatcher" lazilyLoadIfNecessary:YES]; +} + +@end diff --git a/packages/react-native/React/Base/RCTEventDispatcherProtocol.h b/packages/react-native/React/Base/RCTEventDispatcherProtocol.h index 4125e9829443a0..170135bb64b7b9 100644 --- a/packages/react-native/React/Base/RCTEventDispatcherProtocol.h +++ b/packages/react-native/React/Base/RCTEventDispatcherProtocol.h @@ -8,6 +8,7 @@ #import #import +#import /** * The threshold at which text inputs will start warning that the JS thread @@ -132,3 +133,9 @@ typedef NS_ENUM(NSInteger, RCTTextEventType) { - (id)eventDispatcher; @end + +@interface RCTBridgeProxy (RCTEventDispatcher) + +- (id)eventDispatcher; + +@end diff --git a/packages/react-native/React/CoreModules/RCTAccessibilityManager.h b/packages/react-native/React/CoreModules/RCTAccessibilityManager.h index 707d5766dd4c53..01af72e81420ff 100644 --- a/packages/react-native/React/CoreModules/RCTAccessibilityManager.h +++ b/packages/react-native/React/CoreModules/RCTAccessibilityManager.h @@ -9,6 +9,7 @@ #import #import +#import extern NSString *const RCTAccessibilityManagerDidUpdateMultiplierNotification; // posted when multiplier is changed @@ -34,3 +35,9 @@ extern NSString *const RCTAccessibilityManagerDidUpdateMultiplierNotification; / @property (nonatomic, readonly) RCTAccessibilityManager *accessibilityManager; @end + +@interface RCTBridgeProxy (RCTAccessibilityManager) + +@property (nonatomic, readonly) RCTAccessibilityManager *accessibilityManager; + +@end diff --git a/packages/react-native/React/CoreModules/RCTAccessibilityManager.mm b/packages/react-native/React/CoreModules/RCTAccessibilityManager.mm index d91b2a1980e80c..4aaabcaba30be7 100644 --- a/packages/react-native/React/CoreModules/RCTAccessibilityManager.mm +++ b/packages/react-native/React/CoreModules/RCTAccessibilityManager.mm @@ -405,6 +405,15 @@ - (RCTAccessibilityManager *)accessibilityManager @end +@implementation RCTBridgeProxy (RCTAccessibilityManager) + +- (RCTAccessibilityManager *)accessibilityManager +{ + return [self moduleForClass:[RCTAccessibilityManager class]]; +} + +@end + Class RCTAccessibilityManagerCls(void) { return RCTAccessibilityManager.class; diff --git a/packages/react-native/React/CoreModules/RCTDevMenu.h b/packages/react-native/React/CoreModules/RCTDevMenu.h index eedfff15688171..b9b76f63e6e385 100644 --- a/packages/react-native/React/CoreModules/RCTDevMenu.h +++ b/packages/react-native/React/CoreModules/RCTDevMenu.h @@ -9,6 +9,7 @@ #import #import +#import #import #if RCT_DEV_MENU @@ -108,3 +109,9 @@ typedef NSString * (^RCTDevMenuItemTitleBlock)(void); @property (nonatomic, readonly) RCTDevMenu *devMenu; @end + +@interface RCTBridgeProxy (RCTDevMenu) + +@property (nonatomic, readonly) RCTDevMenu *devMenu; + +@end diff --git a/packages/react-native/React/CoreModules/RCTDevMenu.mm b/packages/react-native/React/CoreModules/RCTDevMenu.mm index f40cf4febf25af..1a8b81ead4d23b 100644 --- a/packages/react-native/React/CoreModules/RCTDevMenu.mm +++ b/packages/react-native/React/CoreModules/RCTDevMenu.mm @@ -563,6 +563,19 @@ - (RCTDevMenu *)devMenu @end +@implementation RCTBridgeProxy (RCTDevMenu) + +- (RCTDevMenu *)devMenu +{ +#if RCT_DEV_MENU + return [self moduleForClass:[RCTDevMenu class]]; +#else + return nil; +#endif +} + +@end + Class RCTDevMenuCls(void) { return RCTDevMenu.class; diff --git a/packages/react-native/React/CoreModules/RCTDevSettings.h b/packages/react-native/React/CoreModules/RCTDevSettings.h index c576e372f4b037..35a24b5d9191b2 100644 --- a/packages/react-native/React/CoreModules/RCTDevSettings.h +++ b/packages/react-native/React/CoreModules/RCTDevSettings.h @@ -6,6 +6,7 @@ */ #import +#import #import #import #import @@ -114,6 +115,12 @@ @end +@interface RCTBridgeProxy (RCTDevSettings) + +@property (nonatomic, readonly) RCTDevSettings *devSettings; + +@end + // In debug builds, the dev menu is enabled by default but it is further customizable using this method. // However, this method only has an effect in builds where the dev menu is actually compiled in. // (i.e. RCT_DEV or RCT_DEV_MENU is set) diff --git a/packages/react-native/React/CoreModules/RCTDevSettings.mm b/packages/react-native/React/CoreModules/RCTDevSettings.mm index d9b03ed38744b2..f95e963fb29d5b 100644 --- a/packages/react-native/React/CoreModules/RCTDevSettings.mm +++ b/packages/react-native/React/CoreModules/RCTDevSettings.mm @@ -633,6 +633,21 @@ - (RCTDevSettings *)devSettings @end +@implementation RCTBridgeProxy (RCTDevSettings) + +- (RCTDevSettings *)devSettings +{ +#if RCT_REMOTE_PROFILE + return [self moduleForClass:[RCTDevSettings class]]; +#elif RCT_DEV_MENU + return devSettingsMenuEnabled ? [self moduleForClass:[RCTDevSettings class]] : nil; +#else + return nil; +#endif +} + +@end + Class RCTDevSettingsCls(void) { return RCTDevSettings.class; diff --git a/packages/react-native/React/CoreModules/RCTRedBox.h b/packages/react-native/React/CoreModules/RCTRedBox.h index 5d67a2a57a914c..e4eed8732c40e9 100644 --- a/packages/react-native/React/CoreModules/RCTRedBox.h +++ b/packages/react-native/React/CoreModules/RCTRedBox.h @@ -9,6 +9,7 @@ #import #import +#import #import @class RCTJSStackFrame; @@ -57,3 +58,9 @@ typedef void (^RCTRedBoxButtonPressHandler)(void); @property (nonatomic, readonly) RCTRedBox *redBox; @end + +@interface RCTBridgeProxy (RCTRedBox) + +@property (nonatomic, readonly) RCTRedBox *redBox; + +@end diff --git a/packages/react-native/React/CoreModules/RCTRedBox.mm b/packages/react-native/React/CoreModules/RCTRedBox.mm index c80731cea7d636..dc58594a80702c 100644 --- a/packages/react-native/React/CoreModules/RCTRedBox.mm +++ b/packages/react-native/React/CoreModules/RCTRedBox.mm @@ -702,6 +702,15 @@ - (RCTRedBox *)redBox @end +@implementation RCTBridgeProxy (RCTRedBox) + +- (RCTRedBox *)redBox +{ + return RCTRedBoxGetEnabled() ? [self moduleForClass:[RCTRedBox class]] : nil; +} + +@end + #else // Disabled @interface RCTRedBox () @@ -787,6 +796,15 @@ - (RCTRedBox *)redBox @end +@implementation RCTBridgeProxy (RCTRedBox) + +- (RCTRedBox *)redBox +{ + return nil; +} + +@end + #endif Class RCTRedBoxCls(void) diff --git a/packages/react-native/React/CoreModules/RCTWebSocketModule.h b/packages/react-native/React/CoreModules/RCTWebSocketModule.h index 0e39e18f990c50..f92bfc42e3aea3 100644 --- a/packages/react-native/React/CoreModules/RCTWebSocketModule.h +++ b/packages/react-native/React/CoreModules/RCTWebSocketModule.h @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. */ +#import #import NS_ASSUME_NONNULL_BEGIN @@ -32,4 +33,10 @@ NS_ASSUME_NONNULL_BEGIN @end +@interface RCTBridgeProxy (RCTWebSocketModule) + +- (RCTWebSocketModule *)webSocketModule; + +@end + NS_ASSUME_NONNULL_END diff --git a/packages/react-native/React/CoreModules/RCTWebSocketModule.mm b/packages/react-native/React/CoreModules/RCTWebSocketModule.mm index 4451b9460e5bac..3fd7238911f559 100644 --- a/packages/react-native/React/CoreModules/RCTWebSocketModule.mm +++ b/packages/react-native/React/CoreModules/RCTWebSocketModule.mm @@ -207,6 +207,15 @@ - (RCTWebSocketModule *)webSocketModule @end +@implementation RCTBridgeProxy (RCTWebSocketModule) + +- (RCTWebSocketModule *)webSocketModule +{ + return [self moduleForClass:[RCTWebSocketModule class]]; +} + +@end + Class RCTWebSocketModuleCls(void) { return RCTWebSocketModule.class; diff --git a/packages/react-native/React/Modules/RCTSurfacePresenterStub.h b/packages/react-native/React/Modules/RCTSurfacePresenterStub.h index 70510c181279cc..9cf334854744ca 100644 --- a/packages/react-native/React/Modules/RCTSurfacePresenterStub.h +++ b/packages/react-native/React/Modules/RCTSurfacePresenterStub.h @@ -8,6 +8,7 @@ #import #import +#import @protocol RCTSurfaceProtocol; @@ -44,4 +45,11 @@ NS_ASSUME_NONNULL_BEGIN @end +@interface RCTBridgeProxy (RCTSurfacePresenterStub) + +- (id)surfacePresenter; +- (void)setSurfacePresenter:(id)presenter; + +@end + NS_ASSUME_NONNULL_END diff --git a/packages/react-native/React/Modules/RCTSurfacePresenterStub.m b/packages/react-native/React/Modules/RCTSurfacePresenterStub.m index e4e0ec7404be59..07524d583ea422 100644 --- a/packages/react-native/React/Modules/RCTSurfacePresenterStub.m +++ b/packages/react-native/React/Modules/RCTSurfacePresenterStub.m @@ -20,3 +20,17 @@ - (void)setSurfacePresenter:(id)surfacePresenter } @end + +@implementation RCTBridgeProxy (RCTSurfacePresenterStub) + +- (id)surfacePresenter +{ + return objc_getAssociatedObject(self, @selector(surfacePresenter)); +} + +- (void)setSurfacePresenter:(id)surfacePresenter +{ + objc_setAssociatedObject(self, @selector(surfacePresenter), surfacePresenter, OBJC_ASSOCIATION_RETAIN); +} + +@end diff --git a/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTInstance.mm b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTInstance.mm index 6c76569703cc65..2258edae930bdb 100644 --- a/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTInstance.mm +++ b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTInstance.mm @@ -6,11 +6,13 @@ */ #import "RCTInstance.h" +#import #import #import #import +#import #import #import #import @@ -229,6 +231,21 @@ - (void)_start delegate:self jsInvoker:std::make_shared(bufferedRuntimeExecutor)]; + if (RCTTurboModuleInteropEnabled() && RCTTurboModuleInteropBridgeProxyEnabled()) { + RCTBridgeProxy *bridgeProxy = [[RCTBridgeProxy alloc] + initWithViewRegistry:_bridgeModuleDecorator.viewRegistry_DEPRECATED + moduleRegistry:_bridgeModuleDecorator.moduleRegistry + bundleManager:_bridgeModuleDecorator.bundleManager + callableJSModules:_bridgeModuleDecorator.callableJSModules + dispatchToJSThread:^(dispatch_block_t block) { + __strong __typeof(self) strongSelf = weakSelf; + if (strongSelf && strongSelf->_valid) { + strongSelf->_reactInstance->getBufferedRuntimeExecutor()([=](jsi::Runtime &runtime) { block(); }); + } + }]; + [_turboModuleManager setBridgeProxy:bridgeProxy]; + } + // Initialize RCTModuleRegistry so that TurboModules can require other TurboModules. [_bridgeModuleDecorator.moduleRegistry setTurboModuleRegistry:_turboModuleManager]; diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.h b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.h index 34535f8c381073..4d0872fbab4a69 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.h +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.h @@ -9,6 +9,7 @@ #import +#import #import #import #import @@ -65,6 +66,9 @@ RCT_EXTERN void RCTTurboModuleSetBindingMode(facebook::react::TurboModuleBinding */ - (void)installJSBindingWithRuntimeExecutor:(facebook::react::RuntimeExecutor &)runtimeExecutor; +// TODO: Should we move this into the initializer? +- (void)setBridgeProxy:(RCTBridgeProxy *)bridgeProxy; + - (void)invalidate; @end 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 9b629d8d72fc44..efb232fa13420e 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 @@ -17,6 +17,7 @@ #import #import +#import #import #import #import @@ -198,6 +199,8 @@ @implementation RCTTurboModuleManager { NSDictionary> *_legacyEagerlyInitializedModules; NSDictionary *_legacyEagerlyRegisteredModuleClasses; + + RCTBridgeProxy *_bridgeProxy; } - (instancetype)initWithBridge:(RCTBridge *)bridge @@ -243,6 +246,11 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge return self; } +- (void)setBridgeProxy:(RCTBridgeProxy *)bridgeProxy +{ + _bridgeProxy = bridgeProxy; +} + - (void)notifyAboutTurboModuleSetup:(const char *)name { NSString *moduleName = [[NSString alloc] initWithUTF8String:name]; @@ -441,6 +449,15 @@ - (BOOL)_isLegacyModule:(const char *)moduleName } Class moduleClass = [self _getModuleClassFromName:moduleName]; + return [self _isLegacyModuleClass:moduleClass]; +} + +- (BOOL)_isLegacyModuleClass:(Class)moduleClass +{ + if (RCTTurboModuleInteropForAllTurboModulesEnabled()) { + return YES; + } + return moduleClass != nil && (!RCT_IS_TURBO_MODULE_CLASS(moduleClass) || [moduleClass isSubclassOfClass:RCTCxxModule.class]); } @@ -617,7 +634,7 @@ - (BOOL)_shouldCreateObjCModule:(Class)moduleClass * this method exists to know if we can safely set the bridge to the * NativeModule. */ - if ([module respondsToSelector:@selector(bridge)] && _bridge) { + if ([module respondsToSelector:@selector(bridge)] && (_bridge || _bridgeProxy)) { /** * Just because a NativeModule has the `bridge` method, it doesn't mean * that it has synthesized the bridge in its implementation. Therefore, @@ -634,7 +651,11 @@ - (BOOL)_shouldCreateObjCModule:(Class)moduleClass * generated, so we have have to rely on the KVC API of ObjC to set * the bridge property of these NativeModules. */ - [(id)module setValue:_bridge forKey:@"bridge"]; + if (_bridge) { + [(id)module setValue:_bridge forKey:@"bridge"]; + } else if (_bridgeProxy && [self _isLegacyModuleClass:[module class]]) { + [(id)module setValue:_bridgeProxy forKey:@"bridge"]; + } } @catch (NSException *exception) { RCTLogError( @"%@ has no setter or ivar for its bridge, which is not "