2015-03-23 20:28:42 +00:00
|
|
|
/**
|
|
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD-style license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
*/
|
2015-02-20 04:10:52 +00:00
|
|
|
|
2016-09-19 11:43:06 +00:00
|
|
|
#import "RCTBridge.h"
|
2015-12-15 13:39:30 +00:00
|
|
|
#import "RCTBridge+Private.h"
|
2015-02-20 04:10:52 +00:00
|
|
|
|
|
|
|
#import <objc/runtime.h>
|
|
|
|
|
2015-11-06 13:27:24 +00:00
|
|
|
#import "RCTConvert.h"
|
2015-06-15 14:53:45 +00:00
|
|
|
#import "RCTEventDispatcher.h"
|
2015-02-20 04:10:52 +00:00
|
|
|
#import "RCTLog.h"
|
2016-03-07 17:30:20 +00:00
|
|
|
#import "RCTModuleData.h"
|
2015-06-19 21:59:42 +00:00
|
|
|
#import "RCTPerformanceLogger.h"
|
2016-08-05 18:19:11 +00:00
|
|
|
#import "RCTProfile.h"
|
2016-12-08 04:04:33 +00:00
|
|
|
#import "RCTReloadCommand.h"
|
2015-02-20 04:10:52 +00:00
|
|
|
#import "RCTUtils.h"
|
|
|
|
|
2015-07-28 18:07:45 +00:00
|
|
|
NSString *const RCTJavaScriptWillStartLoadingNotification = @"RCTJavaScriptWillStartLoadingNotification";
|
2015-04-11 22:08:00 +00:00
|
|
|
NSString *const RCTJavaScriptDidLoadNotification = @"RCTJavaScriptDidLoadNotification";
|
2015-05-07 11:29:31 +00:00
|
|
|
NSString *const RCTJavaScriptDidFailToLoadNotification = @"RCTJavaScriptDidFailToLoadNotification";
|
2015-12-15 13:42:45 +00:00
|
|
|
NSString *const RCTDidInitializeModuleNotification = @"RCTDidInitializeModuleNotification";
|
2015-04-11 22:08:00 +00:00
|
|
|
|
2015-11-03 22:45:46 +00:00
|
|
|
static NSMutableArray<Class> *RCTModuleClasses;
|
|
|
|
NSArray<Class> *RCTGetModuleClasses(void)
|
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
|
|
|
{
|
|
|
|
return RCTModuleClasses;
|
|
|
|
}
|
2015-04-10 00:11:13 +00:00
|
|
|
|
2017-02-17 13:47:28 +00:00
|
|
|
void RCTFBQuickPerformanceLoggerConfigureHooks(__unused JSGlobalContextRef ctx) { }
|
|
|
|
|
2015-08-06 22:44:15 +00:00
|
|
|
/**
|
|
|
|
* Register the given class as a bridge module. All modules must be registered
|
|
|
|
* prior to the first bridge initialization.
|
|
|
|
*/
|
|
|
|
void RCTRegisterModule(Class);
|
2015-06-10 10:43:55 +00:00
|
|
|
void RCTRegisterModule(Class moduleClass)
|
|
|
|
{
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
dispatch_once(&onceToken, ^{
|
2015-08-17 14:35:34 +00:00
|
|
|
RCTModuleClasses = [NSMutableArray new];
|
2015-06-10 10:43:55 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
RCTAssert([moduleClass conformsToProtocol:@protocol(RCTBridgeModule)],
|
|
|
|
@"%@ does not conform to the RCTBridgeModule protocol",
|
2015-08-06 22:44:15 +00:00
|
|
|
moduleClass);
|
2015-06-10 10:43:55 +00:00
|
|
|
|
|
|
|
// Register module
|
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
|
|
|
[RCTModuleClasses addObject:moduleClass];
|
2015-06-10 10:43:55 +00:00
|
|
|
}
|
|
|
|
|
2015-04-08 12:42:43 +00:00
|
|
|
/**
|
|
|
|
* This function returns the module name for a given class.
|
|
|
|
*/
|
2015-04-07 14:36:26 +00:00
|
|
|
NSString *RCTBridgeModuleNameForClass(Class cls)
|
2015-02-20 04:10:52 +00:00
|
|
|
{
|
2016-09-19 11:43:06 +00:00
|
|
|
#if RCT_DEBUG
|
2016-03-01 17:44:05 +00:00
|
|
|
RCTAssert([cls conformsToProtocol:@protocol(RCTBridgeModule)],
|
|
|
|
@"Bridge module `%@` does not conform to RCTBridgeModule", cls);
|
2015-08-25 11:27:09 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
NSString *name = [cls moduleName];
|
2015-08-24 10:14:33 +00:00
|
|
|
if (name.length == 0) {
|
2015-04-08 12:42:43 +00:00
|
|
|
name = NSStringFromClass(cls);
|
|
|
|
}
|
2016-09-23 18:12:54 +00:00
|
|
|
|
2015-04-08 12:42:43 +00:00
|
|
|
if ([name hasPrefix:@"RK"]) {
|
2016-09-23 18:12:54 +00:00
|
|
|
name = [name substringFromIndex:2];
|
|
|
|
} else if ([name hasPrefix:@"RCT"]) {
|
|
|
|
name = [name substringFromIndex:3];
|
2015-04-08 12:42:43 +00:00
|
|
|
}
|
2016-09-23 18:12:54 +00:00
|
|
|
|
2015-04-08 12:42:43 +00:00
|
|
|
return name;
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
|
2016-09-19 11:43:06 +00:00
|
|
|
#if RCT_DEBUG
|
|
|
|
void RCTVerifyAllModulesExported(NSArray *extraModules)
|
|
|
|
{
|
|
|
|
// Check for unexported modules
|
|
|
|
unsigned int classCount;
|
|
|
|
Class *classes = objc_copyClassList(&classCount);
|
|
|
|
|
|
|
|
NSMutableSet *moduleClasses = [NSMutableSet new];
|
|
|
|
[moduleClasses addObjectsFromArray:RCTGetModuleClasses()];
|
|
|
|
[moduleClasses addObjectsFromArray:[extraModules valueForKeyPath:@"class"]];
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < classCount; i++) {
|
|
|
|
Class cls = classes[i];
|
|
|
|
Class superclass = cls;
|
|
|
|
while (superclass) {
|
|
|
|
if (class_conformsToProtocol(superclass, @protocol(RCTBridgeModule))) {
|
|
|
|
if ([moduleClasses containsObject:cls]) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify it's not a super-class of one of our moduleClasses
|
|
|
|
BOOL isModuleSuperClass = NO;
|
|
|
|
for (Class moduleClass in moduleClasses) {
|
|
|
|
if ([moduleClass isSubclassOfClass:cls]) {
|
|
|
|
isModuleSuperClass = YES;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (isModuleSuperClass) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
RCTLogWarn(@"Class %@ was not exported. Did you forget to use RCT_EXPORT_MODULE()?", cls);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
superclass = class_getSuperclass(superclass);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(classes);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-12-08 04:04:33 +00:00
|
|
|
@interface RCTBridge () <RCTReloadListener>
|
|
|
|
@end
|
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
@implementation RCTBridge
|
2016-01-04 18:39:07 +00:00
|
|
|
{
|
|
|
|
NSURL *_delegateBundleURL;
|
|
|
|
}
|
2015-04-15 14:07:19 +00:00
|
|
|
|
2015-06-19 11:18:54 +00:00
|
|
|
dispatch_queue_t RCTJSThread;
|
2015-06-15 14:53:45 +00:00
|
|
|
|
2015-06-10 10:43:55 +00:00
|
|
|
+ (void)initialize
|
|
|
|
{
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
|
2015-06-19 11:18:54 +00:00
|
|
|
// Set up JS thread
|
|
|
|
RCTJSThread = (id)kCFNull;
|
2015-06-10 10:43:55 +00:00
|
|
|
});
|
|
|
|
}
|
2015-06-15 14:53:45 +00:00
|
|
|
|
2015-08-19 12:27:43 +00:00
|
|
|
static RCTBridge *RCTCurrentBridgeInstance = nil;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The last current active bridge instance. This is set automatically whenever
|
|
|
|
* the bridge is accessed. It can be useful for static functions or singletons
|
|
|
|
* that need to access the bridge for purposes such as logging, but should not
|
|
|
|
* be relied upon to return any particular instance, due to race conditions.
|
|
|
|
*/
|
|
|
|
+ (instancetype)currentBridge
|
|
|
|
{
|
|
|
|
return RCTCurrentBridgeInstance;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (void)setCurrentBridge:(RCTBridge *)currentBridge
|
|
|
|
{
|
|
|
|
RCTCurrentBridgeInstance = currentBridge;
|
|
|
|
}
|
|
|
|
|
2015-07-28 22:48:46 +00:00
|
|
|
- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)delegate
|
|
|
|
launchOptions:(NSDictionary *)launchOptions
|
|
|
|
{
|
2016-07-07 14:20:03 +00:00
|
|
|
return [self initWithDelegate:delegate
|
|
|
|
bundleURL:nil
|
|
|
|
moduleProvider:nil
|
|
|
|
launchOptions:launchOptions];
|
2015-07-28 22:48:46 +00:00
|
|
|
}
|
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
|
|
|
|
moduleProvider:(RCTBridgeModuleProviderBlock)block
|
|
|
|
launchOptions:(NSDictionary *)launchOptions
|
2016-07-07 14:20:03 +00:00
|
|
|
{
|
|
|
|
return [self initWithDelegate:nil
|
|
|
|
bundleURL:bundleURL
|
|
|
|
moduleProvider:block
|
|
|
|
launchOptions:launchOptions];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)delegate
|
|
|
|
bundleURL:(NSURL *)bundleURL
|
|
|
|
moduleProvider:(RCTBridgeModuleProviderBlock)block
|
|
|
|
launchOptions:(NSDictionary *)launchOptions
|
2015-04-15 14:07:19 +00:00
|
|
|
{
|
2016-07-12 12:51:56 +00:00
|
|
|
if (self = [super init]) {
|
2016-07-07 14:20:03 +00:00
|
|
|
_delegate = delegate;
|
2015-05-04 17:35:49 +00:00
|
|
|
_bundleURL = bundleURL;
|
|
|
|
_moduleProvider = block;
|
|
|
|
_launchOptions = [launchOptions copy];
|
2016-07-22 16:50:48 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
[self setUp];
|
2016-07-22 16:50:48 +00:00
|
|
|
|
2016-06-06 14:57:55 +00:00
|
|
|
RCTExecuteOnMainQueue(^{ [self bindKeys]; });
|
2015-04-15 14:07:19 +00:00
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2015-08-24 10:14:33 +00:00
|
|
|
RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
2015-06-15 14:53:45 +00:00
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
- (void)dealloc
|
2015-04-15 14:07:19 +00:00
|
|
|
{
|
2015-05-04 17:35:49 +00:00
|
|
|
/**
|
|
|
|
* This runs only on the main thread, but crashes the subclass
|
2016-06-06 14:57:55 +00:00
|
|
|
* RCTAssertMainQueue();
|
2015-05-04 17:35:49 +00:00
|
|
|
*/
|
|
|
|
[self invalidate];
|
2015-04-15 14:07:19 +00:00
|
|
|
}
|
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
- (void)bindKeys
|
2015-04-15 14:07:19 +00:00
|
|
|
{
|
2016-06-06 14:57:55 +00:00
|
|
|
RCTAssertMainQueue();
|
2015-05-04 17:35:49 +00:00
|
|
|
|
|
|
|
#if TARGET_IPHONE_SIMULATOR
|
2016-12-08 04:04:33 +00:00
|
|
|
RCTRegisterReloadCommandListener(self);
|
2015-05-04 17:35:49 +00:00
|
|
|
#endif
|
2015-04-15 14:07:19 +00:00
|
|
|
}
|
|
|
|
|
2016-12-08 04:04:33 +00:00
|
|
|
- (void)didReceiveReloadCommand
|
|
|
|
{
|
|
|
|
[self reload];
|
|
|
|
}
|
|
|
|
|
2015-11-25 11:09:00 +00:00
|
|
|
- (NSArray<Class> *)moduleClasses
|
|
|
|
{
|
2015-12-11 09:42:17 +00:00
|
|
|
return self.batchedBridge.moduleClasses;
|
2015-11-25 11:09:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id)moduleForName:(NSString *)moduleName
|
|
|
|
{
|
2015-12-11 09:42:17 +00:00
|
|
|
return [self.batchedBridge moduleForName:moduleName];
|
2015-11-25 11:09:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id)moduleForClass:(Class)moduleClass
|
|
|
|
{
|
|
|
|
return [self moduleForName:RCTBridgeModuleNameForClass(moduleClass)];
|
|
|
|
}
|
|
|
|
|
2016-03-03 10:20:20 +00:00
|
|
|
- (NSArray *)modulesConformingToProtocol:(Protocol *)protocol
|
|
|
|
{
|
|
|
|
NSMutableArray *modules = [NSMutableArray new];
|
|
|
|
for (Class moduleClass in self.moduleClasses) {
|
|
|
|
if ([moduleClass conformsToProtocol:protocol]) {
|
|
|
|
id module = [self moduleForClass:moduleClass];
|
|
|
|
if (module) {
|
|
|
|
[modules addObject:module];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return [modules copy];
|
|
|
|
}
|
|
|
|
|
2016-03-07 17:30:20 +00:00
|
|
|
- (BOOL)moduleIsInitialized:(Class)moduleClass
|
|
|
|
{
|
|
|
|
return [self.batchedBridge moduleIsInitialized:moduleClass];
|
|
|
|
}
|
|
|
|
|
2016-08-05 18:19:11 +00:00
|
|
|
- (void)whitelistedModulesDidChange
|
|
|
|
{
|
|
|
|
[self.batchedBridge whitelistedModulesDidChange];
|
|
|
|
}
|
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
- (void)reload
|
2015-04-15 14:07:19 +00:00
|
|
|
{
|
2015-05-12 14:44:46 +00:00
|
|
|
/**
|
2016-03-07 17:30:20 +00:00
|
|
|
* Any thread
|
2015-05-12 14:44:46 +00:00
|
|
|
*/
|
2015-05-04 17:35:49 +00:00
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
[self invalidate];
|
|
|
|
[self setUp];
|
|
|
|
});
|
2015-04-15 14:07:19 +00:00
|
|
|
}
|
|
|
|
|
2016-09-08 00:24:23 +00:00
|
|
|
- (void)requestReload
|
|
|
|
{
|
|
|
|
[self reload];
|
|
|
|
}
|
|
|
|
|
2017-03-22 01:25:00 +00:00
|
|
|
- (Class)bridgeClass
|
2016-02-29 18:04:15 +00:00
|
|
|
{
|
2017-03-22 01:24:58 +00:00
|
|
|
// In order to facilitate switching between bridges with only build
|
|
|
|
// file changes, this uses reflection to check which bridges are
|
|
|
|
// available. This is a short-term hack until RCTBatchedBridge is
|
|
|
|
// removed.
|
|
|
|
|
|
|
|
Class batchedBridgeClass = objc_lookUpClass("RCTBatchedBridge");
|
|
|
|
Class cxxBridgeClass = objc_lookUpClass("RCTCxxBridge");
|
|
|
|
|
|
|
|
Class implClass = nil;
|
|
|
|
|
|
|
|
if ([self.delegate respondsToSelector:@selector(shouldBridgeUseCxxBridge:)]) {
|
|
|
|
if ([self.delegate shouldBridgeUseCxxBridge:self]) {
|
|
|
|
implClass = cxxBridgeClass;
|
|
|
|
} else {
|
|
|
|
implClass = batchedBridgeClass;
|
|
|
|
}
|
|
|
|
} else if (batchedBridgeClass != nil) {
|
|
|
|
implClass = batchedBridgeClass;
|
|
|
|
} else if (cxxBridgeClass != nil) {
|
|
|
|
implClass = cxxBridgeClass;
|
|
|
|
}
|
|
|
|
|
|
|
|
RCTAssert(implClass != nil, @"No bridge implementation is available, giving up.");
|
2017-03-22 01:25:00 +00:00
|
|
|
return implClass;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setUp
|
|
|
|
{
|
|
|
|
Class bridgeClass = self.bridgeClass;
|
|
|
|
|
|
|
|
RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTBridge setUp]", nil);
|
|
|
|
|
|
|
|
_performanceLogger = [RCTPerformanceLogger new];
|
|
|
|
[_performanceLogger markStartForTag:RCTPLBridgeStartup];
|
|
|
|
[_performanceLogger markStartForTag:RCTPLTTI];
|
|
|
|
|
|
|
|
// Only update bundleURL from delegate if delegate bundleURL has changed
|
|
|
|
NSURL *previousDelegateURL = _delegateBundleURL;
|
|
|
|
_delegateBundleURL = [self.delegate sourceURLForBridge:self];
|
|
|
|
if (_delegateBundleURL && ![_delegateBundleURL isEqual:previousDelegateURL]) {
|
|
|
|
_bundleURL = _delegateBundleURL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sanitize the bundle URL
|
|
|
|
_bundleURL = [RCTConvert NSURL:_bundleURL.absoluteString];
|
|
|
|
|
|
|
|
self.batchedBridge = [[bridgeClass alloc] initWithParentBridge:self];
|
|
|
|
[self.batchedBridge start];
|
|
|
|
|
|
|
|
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
|
2015-05-04 17:35:49 +00:00
|
|
|
}
|
2015-04-15 14:07:19 +00:00
|
|
|
|
2015-05-14 22:56:48 +00:00
|
|
|
- (BOOL)isLoading
|
|
|
|
{
|
2015-12-11 09:42:17 +00:00
|
|
|
return self.batchedBridge.loading;
|
2015-05-14 22:56:48 +00:00
|
|
|
}
|
|
|
|
|
2015-08-21 18:33:04 +00:00
|
|
|
- (BOOL)isValid
|
|
|
|
{
|
2015-12-11 09:42:17 +00:00
|
|
|
return self.batchedBridge.valid;
|
2015-08-21 18:33:04 +00:00
|
|
|
}
|
|
|
|
|
2015-12-11 14:54:56 +00:00
|
|
|
- (BOOL)isBatchActive
|
|
|
|
{
|
|
|
|
return [_batchedBridge isBatchActive];
|
|
|
|
}
|
|
|
|
|
2015-05-04 17:35:49 +00:00
|
|
|
- (void)invalidate
|
|
|
|
{
|
2016-02-29 18:04:15 +00:00
|
|
|
RCTBridge *batchedBridge = self.batchedBridge;
|
2015-12-11 09:42:17 +00:00
|
|
|
self.batchedBridge = nil;
|
2015-04-15 14:07:19 +00:00
|
|
|
|
2015-12-11 09:42:17 +00:00
|
|
|
if (batchedBridge) {
|
2016-06-06 14:57:55 +00:00
|
|
|
RCTExecuteOnMainQueue(^{
|
2015-12-11 09:42:17 +00:00
|
|
|
[batchedBridge invalidate];
|
2016-06-06 14:57:55 +00:00
|
|
|
});
|
2015-12-11 09:42:17 +00:00
|
|
|
}
|
2015-05-04 17:35:49 +00:00
|
|
|
}
|
2015-04-15 14:07:19 +00:00
|
|
|
|
2015-05-12 14:44:38 +00:00
|
|
|
- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args
|
|
|
|
{
|
2016-07-18 14:12:22 +00:00
|
|
|
NSArray<NSString *> *ids = [moduleDotMethod componentsSeparatedByString:@"."];
|
|
|
|
NSString *module = ids[0];
|
|
|
|
NSString *method = ids[1];
|
|
|
|
[self enqueueJSCall:module method:method args:args completion:NULL];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)enqueueJSCall:(NSString *)module method:(NSString *)method args:(NSArray *)args completion:(dispatch_block_t)completion
|
|
|
|
{
|
|
|
|
[self.batchedBridge enqueueJSCall:module method:method args:args completion:completion];
|
2015-05-12 14:44:38 +00:00
|
|
|
}
|
|
|
|
|
2015-12-08 23:57:34 +00:00
|
|
|
- (void)enqueueCallback:(NSNumber *)cbID args:(NSArray *)args
|
|
|
|
{
|
|
|
|
[self.batchedBridge enqueueCallback:cbID args:args];
|
|
|
|
}
|
|
|
|
|
2016-09-28 21:00:16 +00:00
|
|
|
- (JSValue *)callFunctionOnModule:(NSString *)module
|
|
|
|
method:(NSString *)method
|
|
|
|
arguments:(NSArray *)arguments
|
|
|
|
error:(NSError **)error
|
|
|
|
{
|
|
|
|
return [self.batchedBridge callFunctionOnModule:module method:method arguments:arguments error:error];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-04-15 14:07:19 +00:00
|
|
|
@end
|