RN iOS: Support View Manager Commands that are strings

Summary:
Right now JS triggers a view manager command with the following code:

```
UIManager.dispatchViewManagerCommand(
  nullthrows(this.scrollResponderGetScrollableNode()),
  UIManager.getViewManagerConfig('RCTScrollView').Commands.scrollTo,
  [x || 0, y || 0, animated !== false],
);
```

As we want to get rid of calls to UIManager, we need to stop looking for the integer defined in native from JavaScript. We will be changing methods like this to be:

```
UIManager.dispatchViewManagerCommand(
  nullthrows(this.scrollResponderGetScrollableNode()),
  'scrollTo',
  [x || 0, y || 0, animated !== false],
);
```

We need to support ints and Strings to be backwards compatible, but ints will be deprecated.

This is the same change as made for Android here: 3cae6fa950

Reviewed By: PeteTheHeat

Differential Revision: D15983041

fbshipit-source-id: 6cb0f3001553d1f9d26e7e8fb5481e16fbca6847
This commit is contained in:
Eli White 2019-06-25 14:23:41 -07:00 коммит произвёл Facebook Github Bot
Родитель 1dbc38ad93
Коммит 0a34fb78ab
3 изменённых файлов: 69 добавлений и 32 удалений

Просмотреть файл

@ -44,6 +44,12 @@ typedef id<RCTBridgeModule>(^RCTBridgeModuleProvider)(void);
*/
@property (nonatomic, copy, readonly) NSArray<id<RCTBridgeMethod>> *methods;
/**
* Returns a map of the module methods. Note that this will gather the methods the first
* time it is called and then memoize the results.
*/
@property (nonatomic, copy, readonly) NSDictionary<NSString *, id<RCTBridgeMethod>> *methodsByName;
/**
* Returns the module's constants, if it exports any
*/

Просмотреть файл

@ -28,6 +28,7 @@
}
@synthesize methods = _methods;
@synthesize methodsByName = _methodsByName;
@synthesize instance = _instance;
@synthesize methodQueue = _methodQueue;
@ -225,6 +226,47 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init);
}
}
- (void) calculateMethods
{
if (_methods && _methodsByName) {
return;
}
NSMutableArray<id<RCTBridgeMethod>> *moduleMethods = [NSMutableArray new];
NSMutableDictionary<NSString *, id<RCTBridgeMethod>> *moduleMethodsByName = [NSMutableDictionary new];
if ([_moduleClass instancesRespondToSelector:@selector(methodsToExport)]) {
[moduleMethods addObjectsFromArray:[self.instance methodsToExport]];
}
unsigned int methodCount;
Class cls = _moduleClass;
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)(_moduleClass, selector);
id<RCTBridgeMethod> moduleMethod = [[RCTModuleMethod alloc] initWithExportedMethod:exportedMethod
moduleClass:_moduleClass];
NSString *str = [NSString stringWithUTF8String:moduleMethod.JSMethodName];
[moduleMethodsByName setValue:moduleMethod forKey:str];
[moduleMethods addObject:moduleMethod];
}
}
free(methods);
cls = class_getSuperclass(cls);
}
_methods = [moduleMethods copy];
_methodsByName = [moduleMethodsByName copy];
}
#pragma mark - public getters
- (BOOL)hasInstance
@ -267,37 +309,14 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init);
- (NSArray<id<RCTBridgeMethod>> *)methods
{
if (!_methods) {
NSMutableArray<id<RCTBridgeMethod>> *moduleMethods = [NSMutableArray new];
[self calculateMethods];
return _methods;
}
if ([_moduleClass instancesRespondToSelector:@selector(methodsToExport)]) {
[moduleMethods addObjectsFromArray:[self.instance methodsToExport]];
}
unsigned int methodCount;
Class cls = _moduleClass;
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)(_moduleClass, selector);
id<RCTBridgeMethod> moduleMethod = [[RCTModuleMethod alloc] initWithExportedMethod:exportedMethod
moduleClass:_moduleClass];
[moduleMethods addObject:moduleMethod];
}
}
free(methods);
cls = class_getSuperclass(cls);
}
_methods = [moduleMethods copy];
}
return _methods;
- (NSDictionary<NSString *, id<RCTBridgeMethod>> *)methodsByName
{
[self calculateMethods];
return _methodsByName;
}
- (void)gatherConstants

Просмотреть файл

@ -1060,7 +1060,7 @@ RCT_EXPORT_METHOD(findSubviewIn:(nonnull NSNumber *)reactTag atPoint:(CGPoint)po
}
RCT_EXPORT_METHOD(dispatchViewManagerCommand:(nonnull NSNumber *)reactTag
commandID:(NSInteger)commandID
commandID:(id /*(NSString or NSNumber) */)commandID
commandArgs:(NSArray<id> *)commandArgs)
{
RCTShadowView *shadowView = _shadowViewRegistry[reactTag];
@ -1086,7 +1086,19 @@ RCT_EXPORT_METHOD(dispatchViewManagerCommand:(nonnull NSNumber *)reactTag
Class managerClass = componentData.managerClass;
RCTModuleData *moduleData = [_bridge moduleDataForName:RCTBridgeModuleNameForClass(managerClass)];
id<RCTBridgeMethod> method = moduleData.methods[commandID];
id<RCTBridgeMethod> method;
if ([commandID isKindOfClass:[NSNumber class]]) {
method = moduleData.methods[[commandID intValue]];
} else if([commandID isKindOfClass:[NSString class]]) {
method = moduleData.methodsByName[commandID];
if (method == nil) {
RCTLogError(@"No command found with name \"%@\"", commandID);
}
} else {
RCTLogError(@"dispatchViewManagerCommand must be called with a string or integer command");
return;
}
NSArray *args = [@[reactTag] arrayByAddingObjectsFromArray:commandArgs];
[method invokeWithBridge:_bridge module:componentData.manager arguments:args];