GT2/Ejectable/node_modules/@unimodules/react-native-adapter/ios/UMReactNativeAdapter/UMNativeModulesProxy/UMNativeModulesProxy.m

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