From 10acccc86d5b296995d6dacdf80b973bb10e4fc4 Mon Sep 17 00:00:00 2001 From: Peter Argany Date: Wed, 31 Mar 2021 16:37:02 -0700 Subject: [PATCH] Handle commands in RCTLegacyViewManagerInteropCoordinator [3/n] Summary: Problem: `RCTLegacyViewManagerInteropCoordinator.mm` handles view commands by looking up `RCTModuleData` from the bridge, and then dispatching that method. In bridgeless mode, `RCTModuleData` doesn't exist. Solution: Instead of relying on `RCTModuleData` (which does a ton of things), manually create a `_moduleMethods` array to store the methods that view manager exposes. This manual creation code was copied from how `RCTModuleData` performs lookup. Changelog: [Internal] Reviewed By: sammy-SC Differential Revision: D27377257 fbshipit-source-id: c3e820808e6aca03bae6486d5510156b39462215 --- .../RCTLegacyViewManagerInteropCoordinator.mm | 67 ++++++++++++++++--- 1 file changed, 58 insertions(+), 9 deletions(-) diff --git a/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm b/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm index cab75ac3882..e651dfc4638 100644 --- a/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm +++ b/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm @@ -12,10 +12,12 @@ #include #include #include +#include #include #include #include #include +#include using namespace facebook::react; @@ -23,13 +25,20 @@ using namespace facebook::react; RCTComponentData *_componentData; __weak RCTBridge *_bridge; /* - Each instnace of `RCTLegacyViewManagerInteropComponentView` registers a block to which events are dispatched. + 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. */ NSMutableDictionary *_eventInterceptors; + + /* + * In bridgeless mode, instead of using the bridge to look up RCTModuleData, + * store that information locally. + */ + NSMutableArray> *_moduleMethods; + NSMutableDictionary> *_moduleMethodsByName; } -- (instancetype)initWithComponentData:(RCTComponentData *)componentData bridge:(RCTBridge *)bridge; +- (instancetype)initWithComponentData:(RCTComponentData *)componentData bridge:(RCTBridge *)bridge { if (self = [super init]) { _componentData = componentData; @@ -81,12 +90,13 @@ using namespace facebook::react; - (void)handleCommand:(NSString *)commandName args:(NSArray *)args reactTag:(NSInteger)tag { Class managerClass = _componentData.managerClass; + [self _lookupModuleMethodsIfNecessary]; RCTModuleData *moduleData = [_bridge.batchedBridge moduleDataForName:RCTBridgeModuleNameForClass(managerClass)]; id method; if ([commandName isKindOfClass:[NSNumber class]]) { - method = moduleData.methods[[commandName intValue]]; + method = moduleData ? moduleData.methods[[commandName intValue]] : _moduleMethods[[commandName intValue]]; } else if ([commandName isKindOfClass:[NSString class]]) { - method = moduleData.methodsByName[commandName]; + method = moduleData ? moduleData.methodsByName[commandName] : _moduleMethodsByName[commandName]; if (method == nil) { RCTLogError(@"No command found with name \"%@\"", commandName); } @@ -96,12 +106,51 @@ using namespace facebook::react; } NSArray *newArgs = [@[ [NSNumber numberWithInteger:tag] ] arrayByAddingObjectsFromArray:args]; - [_bridge.batchedBridge - dispatchBlock:^{ - [method invokeWithBridge:self->_bridge module:self->_componentData.manager arguments:newArgs]; - [self->_bridge.uiManager setNeedsLayout]; + + if (_bridge) { + [_bridge.batchedBridge + dispatchBlock:^{ + [method invokeWithBridge:self->_bridge module:self->_componentData.manager arguments:newArgs]; + [self->_bridge.uiManager setNeedsLayout]; + } + queue:RCTGetUIManagerQueue()]; + } else { + // TODO T86826778 - Figure out which queue this should be dispatched to. + [method invokeWithBridge:nil module:self->_componentData.manager arguments:newArgs]; + } +} + +#pragma mark - Private + +// This is copy-pasta from RCTModuleData. +- (void)_lookupModuleMethodsIfNecessary +{ + if (!_bridge && !_moduleMethods) { + _moduleMethods = [NSMutableArray new]; + _moduleMethodsByName = [NSMutableDictionary new]; + + unsigned int methodCount; + Class cls = _componentData.managerClass; + while (cls && cls != [NSObject class] && cls != [NSProxy class]) { + Method *methods = class_copyMethodList(object_getClass(cls), &methodCount); + + for (unsigned int i = 0; i < methodCount; i++) { + Method method = methods[i]; + SEL selector = method_getName(method); + if ([NSStringFromSelector(selector) hasPrefix:@"__rct_export__"]) { + IMP imp = method_getImplementation(method); + auto exportedMethod = ((const RCTMethodInfo *(*)(id, SEL))imp)(_componentData.managerClass, selector); + id moduleMethod = + [[RCTModuleMethod alloc] initWithExportedMethod:exportedMethod moduleClass:_componentData.managerClass]; + [_moduleMethodsByName setValue:moduleMethod forKey:[NSString stringWithUTF8String:moduleMethod.JSMethodName]]; + [_moduleMethods addObject:moduleMethod]; + } } - queue:RCTGetUIManagerQueue()]; + + free(methods); + cls = class_getSuperclass(cls); + } + } } @end