Interop: Introduce Bridge proxy

Summary:
The TurboModule interop layer on iOS will ship with a Bridge proxy.

The Bridge proxy is an object that will try to simulate the Bridge's APIs, whenever possible, by delegating to Bridgeless abstractions.

## Changes
This diff introduces the Bridge proxy.
This diff starts attaching the Bridge proxy on legacy native module objects.

## How did we polyfill the APIs
The polyfills can be classified into these categories:
- implemented
- custom warning
- custom error
- default error
- deleted

When there was a sane implementation (e.g: the API belonged to [RCTCallableJSModules](https://www.internalfb.com/code/fbsource/[534223aa13d33b595edcb777189618efe20dd167]/xplat/js/react-native-github/React/Base/RCTCallableJSModules.m?lines=11), [RCTModuleRegistry](https://www.internalfb.com/code/fbsource/[9a3ba2b3176b6d1a1f161e33cec51bf892815311]/xplat/js/react-native-github/React/Base/RCTModuleRegistry.m?lines=13), [RCTBundleManager](https://www.internalfb.com/code/fbsource/[91fa1f7f49731a16aedbcd5a6740647dc21ff727]/xplat/js/react-native-github/React/Base/RCTBundleManager.m?lines=13), or [RCTBundleManager](https://www.internalfb.com/code/fbsource/[91fa1f7f49731a16aedbcd5a6740647dc21ff727]/xplat/js/react-native-github/React/Base/RCTBundleManager.m?lines=13)), it was implemented.

When it was safe to no-op the API (e.g: loading), it emit a custom warning.

When it was unsafe to call (i.e: we didn't want people calling the API) (e.g: RCTCxxBridge start), it emit a custom error.

All other APIs emit default errors.

Some APIs cannot emit errors because doing so makes the app not render: I put warnings and nooped those APIs.

I think we will have to tune these polyfills based off production crashes/results.

Reviewed By: mdvacca

Differential Revision: D46084318

fbshipit-source-id: c02535073956597a4bf91c14b1980f653cb6d3df
This commit is contained in:
Ramanpreet Nara
2023-06-15 18:05:55 -07:00
committed by Facebook GitHub Bot
parent 3e3032636d
commit 7bc2b730a8
25 changed files with 711 additions and 2 deletions
@@ -8,6 +8,7 @@
#import <UIKit/UIKit.h>
#import <React/RCTBridge.h>
#import <React/RCTBridgeProxy.h>
#import <React/RCTDefines.h>
#import <React/RCTImageCache.h>
#import <React/RCTImageDataDecoder.h>
@@ -34,3 +35,9 @@
@property (nonatomic, readonly) RCTImageLoader *imageLoader;
@end
@interface RCTBridgeProxy (RCTImageLoader)
@property (nonatomic, readonly) RCTImageLoader *imageLoader;
@end
@@ -1293,6 +1293,15 @@ RCT_EXPORT_METHOD(queryCache
@end
@implementation RCTBridgeProxy (RCTImageLoader)
- (RCTImageLoader *)imageLoader
{
return [self moduleForClass:[RCTImageLoader class]];
}
@end
Class RCTImageLoaderCls(void)
{
return RCTImageLoader.class;
@@ -8,6 +8,7 @@
#import <UIKit/UIKit.h>
#import <React/RCTBridge.h>
#import <React/RCTBridgeProxy.h>
#import <React/RCTURLRequestHandler.h>
@interface RCTImageStoreManager : NSObject <RCTURLRequestHandler>
@@ -44,3 +45,9 @@
@property (nonatomic, readonly) RCTImageStoreManager *imageStoreManager;
@end
@interface RCTBridgeProxy (RCTImageStoreManager)
@property (nonatomic, readonly) RCTImageStoreManager *imageStoreManager;
@end
@@ -254,6 +254,15 @@ RCT_EXPORT_METHOD(addImageFromBase64
@end
@implementation RCTBridgeProxy (RCTImageStoreManager)
- (RCTImageStoreManager *)imageStoreManager
{
return [self moduleForClass:[RCTImageStoreManager class]];
}
@end
Class RCTImageStoreManagerCls(void)
{
return RCTImageStoreManager.class;
@@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTBridgeProxy.h>
#import <React/RCTEventEmitter.h>
#import <React/RCTNetworkTask.h>
#import <React/RCTURLRequestHandler.h>
@@ -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:
@@ -762,6 +762,15 @@ RCT_EXPORT_METHOD(clearCookies : (RCTResponseSenderBlock)responseSender)
@end
@implementation RCTBridgeProxy (RCTNetworking)
- (RCTNetworking *)networking
{
return [self moduleForClass:[RCTNetworking class]];
}
@end
Class RCTNetworkingCls(void)
{
return RCTNetworking.class;
@@ -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 <Foundation/Foundation.h>
#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
@@ -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 <React/RCTBridge+Private.h>
#import <React/RCTBridge.h>
#import <React/RCTLog.h>
#import <React/RCTUIManager.h>
#import <jsi/jsi.h>
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<Class> *)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<NSString *> *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<RCTBridgeDelegate>)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<RCTBridgeModule>)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<Class> *)newModules
{
[self
logError:
@"This API is unsupported. Please return all module classes from your app's RCTTurboModuleManagerDelegate getModuleClassFromName:. Nooping."
cmd:_cmd];
}
- (void)updateModuleWithInstance:(id<RCTBridgeModule>)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
@@ -28,3 +28,12 @@ NSString *RCTNormalizeInputEventName(NSString *eventName)
}
@end
@implementation RCTBridgeProxy (RCTEventDispatcher)
- (id<RCTEventDispatcherProtocol>)eventDispatcher
{
return [self moduleForName:@"EventDispatcher" lazilyLoadIfNecessary:YES];
}
@end
@@ -8,6 +8,7 @@
#import <UIKit/UIKit.h>
#import <React/RCTBridge.h>
#import <React/RCTBridgeProxy.h>
/**
* The threshold at which text inputs will start warning that the JS thread
@@ -132,3 +133,9 @@ typedef NS_ENUM(NSInteger, RCTTextEventType) {
- (id<RCTEventDispatcherProtocol>)eventDispatcher;
@end
@interface RCTBridgeProxy (RCTEventDispatcher)
- (id<RCTEventDispatcherProtocol>)eventDispatcher;
@end
@@ -9,6 +9,7 @@
#import <React/RCTBridge.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTBridgeProxy.h>
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
@@ -405,6 +405,15 @@ void RCTAccessibilityManagerSetIsVoiceOverEnabled(
@end
@implementation RCTBridgeProxy (RCTAccessibilityManager)
- (RCTAccessibilityManager *)accessibilityManager
{
return [self moduleForClass:[RCTAccessibilityManager class]];
}
@end
Class RCTAccessibilityManagerCls(void)
{
return RCTAccessibilityManager.class;
@@ -9,6 +9,7 @@
#import <React/RCTBridge.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTBridgeProxy.h>
#import <React/RCTDefines.h>
#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
@@ -563,6 +563,19 @@ RCT_EXPORT_METHOD(setHotLoadingEnabled : (BOOL)enabled)
@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;
@@ -6,6 +6,7 @@
*/
#import <React/RCTBridge.h>
#import <React/RCTBridgeProxy.h>
#import <React/RCTDefines.h>
#import <React/RCTEventEmitter.h>
#import <React/RCTInitializing.h>
@@ -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)
@@ -633,6 +633,21 @@ RCT_EXPORT_METHOD(addMenuItem : (NSString *)title)
@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;
@@ -9,6 +9,7 @@
#import <React/RCTBridge.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTBridgeProxy.h>
#import <React/RCTErrorCustomizer.h>
@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
@@ -702,6 +702,15 @@ RCT_EXPORT_METHOD(dismiss)
@end
@implementation RCTBridgeProxy (RCTRedBox)
- (RCTRedBox *)redBox
{
return RCTRedBoxGetEnabled() ? [self moduleForClass:[RCTRedBox class]] : nil;
}
@end
#else // Disabled
@interface RCTRedBox () <NativeRedBoxSpec>
@@ -787,6 +796,15 @@ RCT_EXPORT_METHOD(dismiss)
@end
@implementation RCTBridgeProxy (RCTRedBox)
- (RCTRedBox *)redBox
{
return nil;
}
@end
#endif
Class RCTRedBoxCls(void)
@@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTBridgeProxy.h>
#import <React/RCTEventEmitter.h>
NS_ASSUME_NONNULL_BEGIN
@@ -32,4 +33,10 @@ NS_ASSUME_NONNULL_BEGIN
@end
@interface RCTBridgeProxy (RCTWebSocketModule)
- (RCTWebSocketModule *)webSocketModule;
@end
NS_ASSUME_NONNULL_END
@@ -207,6 +207,15 @@ RCT_EXPORT_METHOD(close : (double)code reason : (NSString *)reason socketID : (d
@end
@implementation RCTBridgeProxy (RCTWebSocketModule)
- (RCTWebSocketModule *)webSocketModule
{
return [self moduleForClass:[RCTWebSocketModule class]];
}
@end
Class RCTWebSocketModuleCls(void)
{
return RCTWebSocketModule.class;
@@ -8,6 +8,7 @@
#import <objc/runtime.h>
#import <React/RCTBridge.h>
#import <React/RCTBridgeProxy.h>
@protocol RCTSurfaceProtocol;
@@ -44,4 +45,11 @@ NS_ASSUME_NONNULL_BEGIN
@end
@interface RCTBridgeProxy (RCTSurfacePresenterStub)
- (id<RCTSurfacePresenterStub>)surfacePresenter;
- (void)setSurfacePresenter:(id<RCTSurfacePresenterStub>)presenter;
@end
NS_ASSUME_NONNULL_END
@@ -20,3 +20,17 @@
}
@end
@implementation RCTBridgeProxy (RCTSurfacePresenterStub)
- (id<RCTSurfacePresenterStub>)surfacePresenter
{
return objc_getAssociatedObject(self, @selector(surfacePresenter));
}
- (void)setSurfacePresenter:(id<RCTSurfacePresenterStub>)surfacePresenter
{
objc_setAssociatedObject(self, @selector(surfacePresenter), surfacePresenter, OBJC_ASSOCIATION_RETAIN);
}
@end
@@ -6,11 +6,13 @@
*/
#import "RCTInstance.h"
#import <React/RCTBridgeProxy.h>
#import <memory>
#import <React/NSDataBigString.h>
#import <React/RCTAssert.h>
#import <React/RCTBridge.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTBridgeModuleDecorator.h>
#import <React/RCTComponentViewFactory.h>
@@ -229,6 +231,21 @@ void RCTInstanceSetRuntimeDiagnosticFlags(NSString *flags)
delegate:self
jsInvoker:std::make_shared<BridgelessJSCallInvoker>(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];
@@ -9,6 +9,7 @@
#import <memory>
#import <React/RCTBridgeProxy.h>
#import <React/RCTDefines.h>
#import <React/RCTTurboModuleRegistry.h>
#import <ReactCommon/RuntimeExecutor.h>
@@ -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
@@ -17,6 +17,7 @@
#import <React/RCTBridge+Private.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTBridgeProxy.h>
#import <React/RCTConstants.h>
#import <React/RCTCxxModule.h>
#import <React/RCTInitializing.h>
@@ -198,6 +199,8 @@ static Class getFallbackClassFromName(const char *name)
NSDictionary<NSString *, id<RCTBridgeModule>> *_legacyEagerlyInitializedModules;
NSDictionary<NSString *, Class> *_legacyEagerlyRegisteredModuleClasses;
RCTBridgeProxy *_bridgeProxy;
}
- (instancetype)initWithBridge:(RCTBridge *)bridge
@@ -243,6 +246,11 @@ static Class getFallbackClassFromName(const char *name)
return self;
}
- (void)setBridgeProxy:(RCTBridgeProxy *)bridgeProxy
{
_bridgeProxy = bridgeProxy;
}
- (void)notifyAboutTurboModuleSetup:(const char *)name
{
NSString *moduleName = [[NSString alloc] initWithUTF8String:name];
@@ -441,6 +449,15 @@ static Class getFallbackClassFromName(const char *name)
}
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 @@ static Class getFallbackClassFromName(const char *name)
* 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 @@ static Class getFallbackClassFromName(const char *name)
* 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 "