1// Copyright 2015-present 650 Industries. All rights reserved.
2
3#import "EXNativeModuleIntrospection.h"
4
5#import <React/RCTBridge.h>
6#import <React/RCTBridgeMethod.h>
7#import <React/RCTModuleData.h>
8
9@implementation EXNativeModuleIntrospection
10
11@synthesize bridge = _bridge;
12
13RCT_EXPORT_MODULE(ExpoNativeModuleIntrospection)
14
15RCT_REMAP_METHOD(getNativeModuleNamesAsync,
16                 nativeModuleNamesWithResolver:(RCTPromiseResolveBlock)resolve
17                 rejecter:(RCTPromiseRejectBlock)reject) {
18  NSError *error;
19  NSDictionary<NSString *, RCTModuleData *> *moduleDataByName = [self _moduleDataFromBridge:&error];
20  if (!moduleDataByName) {
21    reject(error.domain, error.userInfo[NSLocalizedDescriptionKey], error);
22    return;
23  }
24
25  NSArray<NSString *> *moduleNames = moduleDataByName.allKeys;
26  resolve(moduleNames);
27}
28
29RCT_REMAP_METHOD(introspectNativeModuleAsync,
30                 introspectNativeModule:(NSString *)name
31                 resolver:(RCTPromiseResolveBlock)resolve
32                 rejecter:(RCTPromiseRejectBlock)reject) {
33  NSError *error;
34  NSDictionary<NSString *, RCTModuleData *> *moduleDataByName = [self _moduleDataFromBridge:&error];
35  if (!moduleDataByName) {
36    reject(error.domain, error.userInfo[NSLocalizedDescriptionKey], error);
37    return;
38  }
39
40  RCTModuleData *moduleData = moduleDataByName[name];
41  if (!moduleData) {
42    resolve(nil);
43    return;
44  }
45
46  NSArray<id<RCTBridgeMethod>> *moduleMethods = moduleData.methods;
47  NSMutableDictionary<NSString *, NSDictionary<NSString *, id> *> *methodDescriptions = [NSMutableDictionary dictionaryWithCapacity:moduleMethods.count];
48  for (id<RCTBridgeMethod> method in moduleMethods) {
49    NSString *methodName = [NSString stringWithCString:method.JSMethodName encoding:NSASCIIStringEncoding];
50    NSString *functionType = [NSString stringWithCString:RCTFunctionDescriptorFromType(method.functionType)
51                                                encoding:NSASCIIStringEncoding];
52    methodDescriptions[methodName] = @{ @"type": functionType };
53  }
54
55  resolve(@{ @"methods": methodDescriptions });
56}
57
58- (NSDictionary<NSString *, RCTModuleData *> *)_moduleDataFromBridge:(NSError **)error {
59  RCTBridge *bridge = _bridge;
60  if (![NSStringFromClass(bridge.class) isEqualToString:@"RCTCxxBridge"]) {
61    if (error) {
62      *error = [NSError errorWithDomain:@"E_NATIVEMODULEINTROSPECTION_INCOMPATIBLE_BRIDGE" code:0 userInfo:@{
63         NSLocalizedDescriptionKey: @"Native module introspection is compatible only with the C++ bridge",
64      }];
65    }
66    return nil;
67  }
68
69  NSDictionary<NSString *, RCTModuleData *> *moduleDataByName;
70  @try {
71    moduleDataByName = [bridge valueForKey:@"_moduleDataByName"];
72  }
73  @catch (NSException *e) {
74    if (![e.name isEqualToString:NSUndefinedKeyException]) {
75      @throw;
76    }
77
78    if (error) {
79      NSString *variableName = e.userInfo[@"NSUnknownUserInfoKey"];
80      *error = [NSError errorWithDomain:@"E_NATIVEMODULEINTROSPECTION_INCOMPATIBLE_BRIDGE" code:0 userInfo:@{
81        NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Bridge does not define expected variable: %@", variableName],
82      }];
83    }
84    return nil;
85  }
86
87  return moduleDataByName;
88}
89
90@end
91
92