161 lines
7.3 KiB
Objective-C
161 lines
7.3 KiB
Objective-C
// Copyright 2018-present 650 Industries. All rights reserved.
|
|
|
|
#import <UMReactNativeAdapter/UMNativeModulesProxy.h>
|
|
#import <objc/runtime.h>
|
|
#import <React/RCTLog.h>
|
|
#import <UMCore/UMEventEmitter.h>
|
|
#import <UMCore/UMViewManager.h>
|
|
#import <UMReactNativeAdapter/UMViewManagerAdapter.h>
|
|
#import <UMReactNativeAdapter/UMViewManagerAdapterClassesRegistry.h>
|
|
|
|
static const NSString *exportedMethodsNamesKeyPath = @"exportedMethods";
|
|
static const NSString *viewManagersNamesKeyPath = @"viewManagersNames";
|
|
static const NSString *exportedConstantsKeyPath = @"modulesConstants";
|
|
|
|
static const NSString *methodInfoKeyKey = @"key";
|
|
static const NSString *methodInfoNameKey = @"name";
|
|
static const NSString *methodInfoArgumentsCountKey = @"argumentsCount";
|
|
|
|
@interface UMNativeModulesProxy ()
|
|
|
|
@property (nonatomic, strong) NSRegularExpression *regexp;
|
|
@property (nonatomic, strong) UMModuleRegistry *umModuleRegistry;
|
|
@property (nonatomic, strong) NSMutableDictionary<const NSString *, NSMutableDictionary<NSString *, NSNumber *> *> *exportedMethodsKeys;
|
|
@property (nonatomic, strong) NSMutableDictionary<const NSString *, NSMutableDictionary<NSNumber *, NSString *> *> *exportedMethodsReverseKeys;
|
|
|
|
@end
|
|
|
|
@implementation UMNativeModulesProxy
|
|
|
|
- (instancetype)initWithModuleRegistry:(UMModuleRegistry *)moduleRegistry
|
|
{
|
|
if (self = [super init]) {
|
|
_umModuleRegistry = moduleRegistry;
|
|
_exportedMethodsKeys = [NSMutableDictionary dictionary];
|
|
_exportedMethodsReverseKeys = [NSMutableDictionary dictionary];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
+ (const NSString *)moduleName
|
|
{
|
|
return @"NativeUnimoduleProxy";
|
|
}
|
|
|
|
# pragma mark - React API
|
|
|
|
+ (BOOL)requiresMainQueueSetup
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
- (NSDictionary *)constantsToExport
|
|
{
|
|
NSMutableDictionary <NSString *, id> *exportedModulesConstants = [NSMutableDictionary dictionary];
|
|
// Grab all the constants exported by modules
|
|
for (UMExportedModule *exportedModule in [_umModuleRegistry getAllExportedModules]) {
|
|
@try {
|
|
exportedModulesConstants[[[exportedModule class] exportedModuleName]] = [exportedModule constantsToExport] ?: [NSNull null];
|
|
} @catch (NSException *exception) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Also add `exportedMethodsNames`
|
|
NSMutableDictionary<const NSString *, NSMutableArray<NSMutableDictionary<const NSString *, id> *> *> *exportedMethodsNamesAccumulator = [NSMutableDictionary dictionary];
|
|
for (UMExportedModule *exportedModule in [_umModuleRegistry getAllExportedModules]) {
|
|
const NSString *exportedModuleName = [[exportedModule class] exportedModuleName];
|
|
exportedMethodsNamesAccumulator[exportedModuleName] = [NSMutableArray array];
|
|
[[exportedModule getExportedMethods] enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull exportedName, NSString * _Nonnull selectorName, BOOL * _Nonnull stop) {
|
|
NSMutableDictionary<const NSString *, id> *methodInfo = [NSMutableDictionary dictionaryWithDictionary:@{
|
|
methodInfoNameKey: exportedName,
|
|
// - 3 is for resolver and rejecter of the promise and the last, empty component
|
|
methodInfoArgumentsCountKey: @([[selectorName componentsSeparatedByString:@":"] count] - 3)
|
|
}];
|
|
[exportedMethodsNamesAccumulator[exportedModuleName] addObject:methodInfo];
|
|
}];
|
|
[self assignExportedMethodsKeys:exportedMethodsNamesAccumulator[exportedModuleName] forModuleName:exportedModuleName];
|
|
}
|
|
|
|
// Also, add `viewManagersNames` for sanity check and testing purposes -- with names we know what managers to mock on UIManager
|
|
NSArray<UMViewManager *> *viewManagers = [_umModuleRegistry getAllViewManagers];
|
|
NSMutableArray<NSString *> *viewManagersNames = [NSMutableArray arrayWithCapacity:[viewManagers count]];
|
|
for (UMViewManager *viewManager in viewManagers) {
|
|
[viewManagersNames addObject:[viewManager viewName]];
|
|
}
|
|
|
|
NSMutableDictionary <NSString *, id> *constantsAccumulator = [NSMutableDictionary dictionary];
|
|
constantsAccumulator[viewManagersNamesKeyPath] = viewManagersNames;
|
|
constantsAccumulator[exportedConstantsKeyPath] = exportedModulesConstants;
|
|
constantsAccumulator[exportedMethodsNamesKeyPath] = exportedMethodsNamesAccumulator;
|
|
|
|
return constantsAccumulator;
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(callMethod:(NSString *)moduleName methodNameOrKey:(id)methodNameOrKey arguments:(NSArray *)arguments resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
{
|
|
UMExportedModule *module = [_umModuleRegistry getExportedModuleForName:moduleName];
|
|
if (module == nil) {
|
|
NSString *reason = [NSString stringWithFormat:@"No exported module was found for name '%@'. Are you sure all the packages are linked correctly?", moduleName];
|
|
reject(@"E_NO_MODULE", reason, nil);
|
|
return;
|
|
}
|
|
|
|
if (!methodNameOrKey) {
|
|
reject(@"E_NO_METHOD", @"No method key or name provided", nil);
|
|
return;
|
|
}
|
|
|
|
NSString *methodName;
|
|
if ([methodNameOrKey isKindOfClass:[NSString class]]) {
|
|
methodName = (NSString *)methodNameOrKey;
|
|
} else if ([methodNameOrKey isKindOfClass:[NSNumber class]]) {
|
|
methodName = _exportedMethodsReverseKeys[moduleName][(NSNumber *)methodNameOrKey];
|
|
} else {
|
|
reject(@"E_INV_MKEY", @"Method key is neither a String nor an Integer -- don't know how to map it to method name.", nil);
|
|
return;
|
|
}
|
|
|
|
dispatch_async([module methodQueue], ^{
|
|
@try {
|
|
[module callExportedMethod:methodName withArguments:arguments resolver:resolve rejecter:reject];
|
|
} @catch (NSException *e) {
|
|
NSString *message = [NSString stringWithFormat:@"An exception was thrown while calling `%@.%@` with arguments `%@`: %@", moduleName, methodName, arguments, e];
|
|
reject(@"E_EXC", message, nil);
|
|
}
|
|
});
|
|
}
|
|
|
|
- (void)assignExportedMethodsKeys:(NSMutableArray<NSMutableDictionary<const NSString *, id> *> *)exportedMethods forModuleName:(const NSString *)moduleName
|
|
{
|
|
if (!_exportedMethodsKeys[moduleName]) {
|
|
_exportedMethodsKeys[moduleName] = [NSMutableDictionary dictionary];
|
|
}
|
|
|
|
if (!_exportedMethodsReverseKeys[moduleName]) {
|
|
_exportedMethodsReverseKeys[moduleName] = [NSMutableDictionary dictionary];
|
|
}
|
|
|
|
for (int i = 0; i < [exportedMethods count]; i++) {
|
|
NSMutableDictionary<const NSString *, id> *methodInfo = exportedMethods[i];
|
|
|
|
if (!methodInfo[(NSString *)methodInfoNameKey] || ![methodInfo[methodInfoNameKey] isKindOfClass:[NSString class]]) {
|
|
NSString *reason = [NSString stringWithFormat:@"Method info of a method of module %@ has no method name.", moduleName];
|
|
@throw [NSException exceptionWithName:@"Empty method name in method info" reason:reason userInfo:nil];
|
|
}
|
|
|
|
NSString *methodName = methodInfo[(NSString *)methodInfoNameKey];
|
|
NSNumber *previousMethodKey = _exportedMethodsKeys[moduleName][methodName];
|
|
if (previousMethodKey) {
|
|
methodInfo[methodInfoKeyKey] = previousMethodKey;
|
|
} else {
|
|
NSNumber *newKey = @([[_exportedMethodsKeys[moduleName] allValues] count]);
|
|
methodInfo[methodInfoKeyKey] = newKey;
|
|
_exportedMethodsKeys[moduleName][methodName] = newKey;
|
|
_exportedMethodsReverseKeys[moduleName][newKey] = methodName;
|
|
}
|
|
}
|
|
}
|
|
|
|
@end
|