Skip to content

Commit

Permalink
Make the Fabric Interop layer work in Bridgeless (facebook#40732)
Browse files Browse the repository at this point in the history
Summary:

This change allows the Fabric Interop Layer to work in bridgeless mode.

Given that the legacy components requires a Bridge to send events and use commands, this change simulates a bridge when it doesn't exists (i.e.: we are in bridgeless mode).

In order to make it work, we had to simulate a few elements from the Bridge and the UIManager.

## Changelog:
[iOS][Fixed] - Make the Fabric interop layer works in bridgeless mode.

## Facebook:
As an alternative approach, we could have created a `protocol RCTBridging`, have the `RCTBridge` conform to that protocol, and create a new type for it.
Practically this would have been much more cumbersome:
1. The [RCTBridge](https://www.internalfb.com/code/fbsource/[916531b9bf7a9943036807f7563c925b4c3e0101]/xplat/js/react-native-github/packages/react-native/React/Base/RCTBridge.h?lines=87-238) interface is quite big. All the props and method should be part of the protocol.
2. Extensions declared on RCTBridge would not have worked. For example, [`RCTBridge (RCTUIManager)`](https://www.internalfb.com/code/fbsource/[916531b9bf7a9943036807f7563c925b4c3e0101]/xplat/js/react-native-github/packages/react-native/React/Modules/RCTUIManager.h?lines=170-174).
3. It would require a major overhaul of the APIs, returning `id<RCTBridging>` in place of **every** function that takes/return an instance of `RCTBridge *`.

Clearly, not a feasible way to go.

Differential Revision: D50079929
  • Loading branch information
Riccardo Cipolleschi authored and facebook-github-bot committed Oct 10, 2023
1 parent 5cec1ea commit e185cf8
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 32 deletions.
14 changes: 11 additions & 3 deletions packages/react-native/React/Base/RCTBridgeProxy.mm
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,11 @@ - (RCTUIManager *)uiManager
return (RCTUIManager *)_uiManagerProxy;
}

- (RCTBridgeProxy *)object
{
return self;
}

/**
* NSProxy setup
*/
Expand Down Expand Up @@ -387,12 +392,14 @@ - (void)logError:(NSString *)message cmd:(SEL)cmd

@implementation RCTUIManagerProxy {
RCTViewRegistry *_viewRegistry;
NSMutableDictionary<NSNumber *, UIView *> *_legacyViewRegistry;
}
- (instancetype)initWithViewRegistry:(RCTViewRegistry *)viewRegistry
{
self = [super self];
if (self) {
_viewRegistry = viewRegistry;
_legacyViewRegistry = [NSMutableDictionary new];
}
return self;
}
Expand All @@ -404,20 +411,21 @@ - (UIView *)viewForReactTag:(NSNumber *)reactTag
{
[self logWarning:@"Please migrate to RCTViewRegistry: @synthesize viewRegistry_DEPRECATED = _viewRegistry_DEPRECATED."
cmd:_cmd];
return [_viewRegistry viewForReactTag:reactTag];
return [_viewRegistry viewForReactTag:reactTag] ? [_viewRegistry viewForReactTag:reactTag]
: [_legacyViewRegistry objectForKey: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."
@"This method isn't implemented faithfully. Please migrate to RCTViewRegistry if possible: @synthesize viewRegistry_DEPRECATED = _viewRegistry_DEPRECATED."
cmd:_cmd];
__weak __typeof(self) weakSelf = self;
RCTExecuteOnMainQueue(^{
__typeof(self) strongSelf = weakSelf;
if (strongSelf) {
block((RCTUIManager *)strongSelf, nil);
block((RCTUIManager *)strongSelf, strongSelf->_legacyViewRegistry);
}
});
}
Expand Down
7 changes: 7 additions & 0 deletions packages/react-native/React/Modules/RCTUIManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#import <React/RCTBridge.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTBridgeProxy.h>
#import <React/RCTInvalidating.h>
#import <React/RCTRootView.h>
#import <React/RCTViewManager.h>
Expand Down Expand Up @@ -173,6 +174,12 @@ RCT_EXTERN NSString *const RCTUIManagerWillUpdateViewsDueToContentSizeMultiplier

@end

@interface RCTBridgeProxy (RCTUIManager)

@property (nonatomic, readonly) RCTUIManager *uiManager;

@end

RCT_EXTERN NSMutableDictionary<NSString *, id> *RCTModuleConstantsForDestructuredComponent(
NSMutableDictionary<NSString *, NSDictionary *> *directEvents,
NSMutableDictionary<NSString *, NSDictionary *> *bubblingEvents,
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native/React/Views/RCTComponentData.m
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ - (RCTViewManager *)manager
object:nil
userInfo:@{@"module" : _bridgelessViewManager}];
}
return _manager ?: _bridgelessViewManager;
return _manager ? _manager : _bridgelessViewManager;
}

RCT_NOT_IMPLEMENTED(-(instancetype)init)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "LegacyViewManagerInteropComponentDescriptor.h"
#include <React/RCTBridge.h>
#include <React/RCTBridgeModuleDecorator.h>
#include <React/RCTBridgeProxy.h>
#include <React/RCTComponentData.h>
#include <React/RCTEventDispatcher.h>
#include <React/RCTModuleData.h>
Expand Down Expand Up @@ -86,6 +87,12 @@ static Class getViewManagerFromComponentName(const std::string &componentName)
bridge = unwrapManagedObjectWeakly(optionalBridge.value());
}

RCTBridgeProxy *bridgeProxy;
auto optionalBridgeProxy = contextContainer->find<std::shared_ptr<void>>("RCTBridgeProxy");
if (optionalBridgeProxy) {
bridgeProxy = unwrapManagedObjectWeakly(optionalBridgeProxy.value());
}

auto optionalEventDispatcher = contextContainer->find<std::shared_ptr<void>>("RCTEventDispatcher");
RCTEventDispatcher *eventDispatcher;
if (optionalEventDispatcher) {
Expand All @@ -104,6 +111,7 @@ static Class getViewManagerFromComponentName(const std::string &componentName)
return wrapManagedObject([[RCTLegacyViewManagerInteropCoordinator alloc]
initWithComponentData:componentData
bridge:bridge
bridgeProxy:bridgeProxy
bridgelessInteropData:bridgeModuleDecorator]);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ NS_ASSUME_NONNULL_BEGIN

@class RCTComponentData;
@class RCTBridge;
@class RCTBridgeProxy;

typedef void (^InterceptorBlock)(std::string eventName, folly::dynamic event);

@interface RCTLegacyViewManagerInteropCoordinator : NSObject

- (instancetype)initWithComponentData:(RCTComponentData *)componentData
bridge:(RCTBridge *)bridge
bridge:(nullable RCTBridge *)bridge
bridgeProxy:(nullable RCTBridgeProxy *)bridgeProxy
bridgelessInteropData:(RCTBridgeModuleDecorator *)bridgelessInteropData;

- (UIView *)createPaperViewWithTag:(NSInteger)tag;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "RCTLegacyViewManagerInteropCoordinator.h"
#include <React/RCTBridge+Private.h>
#include <React/RCTBridgeMethod.h>
#include <React/RCTBridgeProxy.h>
#include <React/RCTComponentData.h>
#include <React/RCTEventDispatcherProtocol.h>
#include <React/RCTFollyConvert.h>
Expand All @@ -16,6 +17,7 @@
#include <React/RCTUIManager.h>
#include <React/RCTUIManagerUtils.h>
#include <React/RCTUtils.h>
#include <React/RCTViewManager.h>
#include <folly/json.h>
#include <objc/runtime.h>

Expand All @@ -25,6 +27,8 @@ @implementation RCTLegacyViewManagerInteropCoordinator {
RCTComponentData *_componentData;
__weak RCTBridge *_bridge;
__weak RCTBridgeModuleDecorator *_bridgelessInteropData;
__weak RCTBridgeProxy *_bridgeProxy;

/*
Each instance of `RCTLegacyViewManagerInteropComponentView` registers a block to which events are dispatched.
This is the container that maps unretained UIView pointer to a block to which the event is dispatched.
Expand All @@ -40,13 +44,16 @@ @implementation RCTLegacyViewManagerInteropCoordinator {
}

- (instancetype)initWithComponentData:(RCTComponentData *)componentData
bridge:(RCTBridge *)bridge
bridge:(nullable RCTBridge *)bridge
bridgeProxy:(nullable RCTBridgeProxy *)bridgeProxy
bridgelessInteropData:(RCTBridgeModuleDecorator *)bridgelessInteropData;
{
if (self = [super init]) {
_componentData = componentData;
_bridge = bridge;
_bridgelessInteropData = bridgelessInteropData;
_bridgeProxy = bridgeProxy;

if (bridgelessInteropData) {
// During bridge mode, RCTBridgeModules will be decorated with these APIs by the bridge.
RCTAssert(
Expand All @@ -62,7 +69,9 @@ - (instancetype)initWithComponentData:(RCTComponentData *)componentData
if (strongSelf) {
InterceptorBlock block = [strongSelf->_eventInterceptors objectForKey:reactTag];
if (block) {
block(std::string([RCTNormalizeInputEventName(eventName) UTF8String]), convertIdToFollyDynamic(event ?: @{}));
block(
std::string([RCTNormalizeInputEventName(eventName) UTF8String]),
convertIdToFollyDynamic(event ? event : @{}));
}
}
};
Expand Down Expand Up @@ -131,15 +140,9 @@ - (void)handleCommand:(NSString *)commandName
NSArray *newArgs = [@[ [NSNumber numberWithInteger:tag] ] arrayByAddingObjectsFromArray:args];

if (_bridge) {
[_bridge.batchedBridge
dispatchBlock:^{
[method invokeWithBridge:self->_bridge module:self->_componentData.manager arguments:newArgs];
[self->_bridge.uiManager setNeedsLayout];
}
queue:RCTGetUIManagerQueue()];
[self _handleCommandsOnBridge:method withArgs:newArgs];
} else {
// TODO T86826778 - Figure out which queue this should be dispatched to.
[method invokeWithBridge:nil module:self->_componentData.manager arguments:newArgs];
[self _handleCommandsOnBridgeless:method withArgs:newArgs];
}
}

Expand Down Expand Up @@ -169,8 +172,37 @@ - (void)removeViewFromRegistryWithTag:(NSInteger)tag
}

#pragma mark - Private
- (void)_handleCommandsOnBridge:(id<RCTBridgeMethod>)method withArgs:(NSArray *)newArgs
{
[_bridge.batchedBridge
dispatchBlock:^{
[method invokeWithBridge:self->_bridge module:self->_componentData.manager arguments:newArgs];
[self->_bridge.uiManager setNeedsLayout];
}
queue:RCTGetUIManagerQueue()];
}

- (void)_handleCommandsOnBridgeless:(id<RCTBridgeMethod>)method withArgs:(NSArray *)newArgs
{
RCTViewManager *componentViewManager = self->_componentData.manager;
[componentViewManager setValue:_bridgeProxy forKey:@"bridge"];

[self->_bridgeProxy.uiManager
addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
[method invokeWithBridge:nil module:componentViewManager arguments:newArgs];
}];
}

- (void)_addUIBlock:(RCTViewManagerUIBlock)block
{
if (_bridge) {
[self _addUIBlockOnBridge:block];
} else {
[self->_bridgeProxy.uiManager addUIBlock:block];
}
}

- (void)_addUIBlockOnBridge:(RCTViewManagerUIBlock)block
{
__weak __typeof__(self) weakSelf = self;
[_bridge.batchedBridge
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,24 +227,23 @@ - (void)_start
RuntimeExecutor bufferedRuntimeExecutor = _reactInstance->getBufferedRuntimeExecutor();
timerManager->setRuntimeExecutor(bufferedRuntimeExecutor);

RCTBridgeProxy *bridgeProxy = RCTTurboModuleInteropEnabled() && RCTTurboModuleInteropBridgeProxyEnabled()
? [[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(); });
}
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(); });
}
registerSegmentWithId:^(NSNumber *segmentId, NSString *path) {
__strong __typeof(self) strongSelf = weakSelf;
if (strongSelf && strongSelf->_valid) {
[strongSelf registerSegmentWithId:segmentId path:path];
}
}]
: nil;
}
registerSegmentWithId:^(NSNumber *segmentId, NSString *path) {
__strong __typeof(self) strongSelf = weakSelf;
if (strongSelf && strongSelf->_valid) {
[strongSelf registerSegmentWithId:segmentId path:path];
}
}];

// Set up TurboModules
_turboModuleManager = [[RCTTurboModuleManager alloc]
Expand All @@ -269,6 +268,7 @@ - (void)_start
facebook::react::wrapManagedObject([_turboModuleManager moduleForName:"RCTEventDispatcher"]));
contextContainer->insert("RCTBridgeModuleDecorator", facebook::react::wrapManagedObject(_bridgeModuleDecorator));
contextContainer->insert("RuntimeScheduler", std::weak_ptr<RuntimeScheduler>(_reactInstance->getRuntimeScheduler()));
contextContainer->insert("RCTBridgeProxy", facebook::react::wrapManagedObject(bridgeProxy));

_surfacePresenter = [[RCTSurfacePresenter alloc]
initWithContextContainer:contextContainer
Expand Down

0 comments on commit e185cf8

Please sign in to comment.