[ReactNative] Add RCTBridgeDelegate

Summary:
Add a new bridge delegate protocol to allow a more flexible bridge configuration.

For now it just support the pre-existent configurations + providing the JavaScript
source to the bridge, that should allow pre-loading sources.
This commit is contained in:
Tadeu Zagallo 2015-07-28 15:48:46 -07:00
parent 068f396c9b
commit 127f7095dc
9 changed files with 167 additions and 72 deletions

View File

@ -14,13 +14,35 @@
#import "AppDelegate.h" #import "AppDelegate.h"
#import "RCTBridge.h"
#import "RCTJavaScriptLoader.h"
#import "RCTRootView.h" #import "RCTRootView.h"
@interface AppDelegate() <RCTBridgeDelegate>
@end
@implementation AppDelegate @implementation AppDelegate
- (BOOL)application:(__unused UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions - (BOOL)application:(__unused UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{ {
NSURL *jsCodeLocation; RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self
launchOptions:launchOptions];
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"UIExplorerApp"];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [[UIViewController alloc] init];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
- (NSURL *)sourceURLForBridge:(__unused RCTBridge *)bridge
{
NSURL *sourceURL;
/** /**
* Loading JavaScript code - uncomment the one you want. * Loading JavaScript code - uncomment the one you want.
@ -36,7 +58,7 @@
* on the same Wi-Fi network. * on the same Wi-Fi network.
*/ */
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/UIExplorer/UIExplorerApp.ios.includeRequire.runModule.bundle?dev=true"]; sourceURL = [NSURL URLWithString:@"http://localhost:8081/Examples/UIExplorer/UIExplorerApp.ios.includeRequire.runModule.bundle?dev=true"];
/** /**
* OPTION 2 * OPTION 2
@ -48,22 +70,20 @@
* then add the `main.jsbundle` file to your project and uncomment this line: * then add the `main.jsbundle` file to your project and uncomment this line:
*/ */
// jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; // sourceURL = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#if RUNNING_ON_CI #if RUNNING_ON_CI
jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; sourceURL = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif #endif
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation return sourceURL;
moduleName:@"UIExplorerApp" }
launchOptions:launchOptions];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; - (void)loadSourceForBridge:(RCTBridge *)bridge
UIViewController *rootViewController = [[UIViewController alloc] init]; withBlock:(RCTSourceLoadBlock)loadCallback
rootViewController.view = rootView; {
self.window.rootViewController = rootViewController; [RCTJavaScriptLoader loadBundleAtURL:[self sourceURLForBridge:bridge]
[self.window makeKeyAndVisible]; onComplete:loadCallback];
return YES;
} }
@end @end

View File

@ -149,7 +149,11 @@ RCT_EXPORT_MODULE()
- (void)executeApplicationScript:(NSString *)script sourceURL:(NSURL *)URL onComplete:(RCTJavaScriptCompleteBlock)onComplete - (void)executeApplicationScript:(NSString *)script sourceURL:(NSURL *)URL onComplete:(RCTJavaScriptCompleteBlock)onComplete
{ {
NSDictionary *message = @{@"method": @"executeApplicationScript", @"url": [URL absoluteString], @"inject": _injectedObjects}; NSDictionary *message = @{
@"method": @"executeApplicationScript",
@"url": RCTNullIfNil([URL absoluteString]),
@"inject": _injectedObjects,
};
[self sendMessage:message waitForReply:^(NSError *error, NSDictionary *reply) { [self sendMessage:message waitForReply:^(NSError *error, NSDictionary *reply) {
onComplete(error); onComplete(error);
}]; }];

View File

@ -156,6 +156,11 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
_parentBridge.bundleURL = bundleURL; _parentBridge.bundleURL = bundleURL;
} }
- (id<RCTBridgeDelegate>)delegate
{
return _parentBridge.delegate;
}
- (BOOL)isLoading - (BOOL)isLoading
{ {
return _loading; return _loading;
@ -172,7 +177,17 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
// Register passed-in module instances // Register passed-in module instances
NSMutableDictionary *preregisteredModules = [[NSMutableDictionary alloc] init]; NSMutableDictionary *preregisteredModules = [[NSMutableDictionary alloc] init];
for (id<RCTBridgeModule> module in self.moduleProvider ? self.moduleProvider() : nil) {
NSArray *extraModules = nil;
if (self.delegate) {
if ([self.delegate respondsToSelector:@selector(extraModulesForBridge:)]) {
extraModules = [self.delegate extraModulesForBridge:_parentBridge];
}
} else if (self.moduleProvider) {
extraModules = self.moduleProvider();
}
for (id<RCTBridgeModule> module in extraModules) {
preregisteredModules[RCTBridgeModuleNameForClass([module class])] = module; preregisteredModules[RCTBridgeModuleNameForClass([module class])] = module;
} }
@ -261,7 +276,6 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
} }
}]; }];
NSURL *bundleURL = _parentBridge.bundleURL;
if (_javaScriptExecutor == nil) { if (_javaScriptExecutor == nil) {
/** /**
@ -270,24 +284,15 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
*/ */
_loading = NO; _loading = NO;
} else if (!bundleURL) {
// Allow testing without a script
dispatch_async(dispatch_get_main_queue(), ^{
_loading = NO;
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification
object:_parentBridge
userInfo:@{ @"bridge": self }];
});
} else { } else {
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptWillStartLoadingNotification [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptWillStartLoadingNotification
object:self object:self
userInfo:@{ @"bridge": self }]; userInfo:@{ @"bridge": self }];
RCTProfileBeginEvent();
RCTPerformanceLoggerStart(RCTPLScriptDownload); RCTPerformanceLoggerStart(RCTPLScriptDownload);
RCTJavaScriptLoader *loader = [[RCTJavaScriptLoader alloc] initWithBridge:self]; RCTProfileBeginEvent();
[loader loadBundleAtURL:bundleURL onComplete:^(NSError *error, NSString *script) { NSURL *sourceURL = self.delegate ? [self.delegate sourceURLForBridge:_parentBridge] : self.bundleURL;
void (^loadCallback)(NSError *, NSString *) = ^(NSError *error, NSString *script) {
RCTPerformanceLoggerEnd(RCTPLScriptDownload); RCTPerformanceLoggerEnd(RCTPLScriptDownload);
RCTProfileEndEvent(@"JavaScript download", @"init,download", @[]); RCTProfileEndEvent(@"JavaScript download", @"init,download", @[]);
@ -306,7 +311,7 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
}); });
RCTSourceCode *sourceCodeModule = self.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])]; RCTSourceCode *sourceCodeModule = self.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])];
sourceCodeModule.scriptURL = bundleURL; sourceCodeModule.scriptURL = sourceURL;
sourceCodeModule.scriptText = script; sourceCodeModule.scriptText = script;
if (error) { if (error) {
@ -326,7 +331,7 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
} else { } else {
[self enqueueApplicationScript:script url:bundleURL onComplete:^(NSError *loadError) { [self enqueueApplicationScript:script url:sourceURL onComplete:^(NSError *loadError) {
if (loadError) { if (loadError) {
[[RCTRedBox sharedInstance] showError:loadError]; [[RCTRedBox sharedInstance] showError:loadError];
@ -345,7 +350,22 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
userInfo:@{ @"bridge": self }]; userInfo:@{ @"bridge": self }];
}]; }];
} }
}]; };
if ([self.delegate respondsToSelector:@selector(loadSourceForBridge:withBlock:)]) {
[self.delegate loadSourceForBridge:_parentBridge withBlock:loadCallback];
} else if (sourceURL) {
[RCTJavaScriptLoader loadBundleAtURL:sourceURL
onComplete:loadCallback];
} else {
// Allow testing without a script
dispatch_async(dispatch_get_main_queue(), ^{
_loading = NO;
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification
object:_parentBridge
userInfo:@{ @"bridge": self }];
});
}
} }
} }

View File

@ -9,6 +9,7 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import "RCTBridgeDelegate.h"
#import "RCTBridgeModule.h" #import "RCTBridgeModule.h"
#import "RCTDefines.h" #import "RCTDefines.h"
#import "RCTFrameUpdate.h" #import "RCTFrameUpdate.h"
@ -70,7 +71,22 @@ RCT_EXTERN NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass);
*/ */
@interface RCTBridge : NSObject <RCTInvalidating> @interface RCTBridge : NSObject <RCTInvalidating>
/** /**
* Creates a new bridge with a custom RCTBridgeDelegate.
*
* All the interaction with the JavaScript context should be done using the bridge
* instance of the RCTBridgeModules. Modules will be automatically instantiated
* using the default contructor, but you can optionally pass in an array of
* pre-initialized module instances if they require additional init parameters
* or configuration.
*/
- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)delegate
launchOptions:(NSDictionary *)launchOptions NS_DESIGNATED_INITIALIZER;
/**
* DEPRECATED: Use initWithDelegate:launchOptions: instead
*
* The designated initializer. This creates a new bridge on top of the specified * The designated initializer. This creates a new bridge on top of the specified
* executor. The bridge should then be used for all subsequent communication * executor. The bridge should then be used for all subsequent communication
* with the JavaScript code running in the executor. Modules will be automatically * with the JavaScript code running in the executor. Modules will be automatically
@ -100,8 +116,17 @@ RCT_EXTERN NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass);
*/ */
@property (nonatomic, strong) NSURL *bundleURL; @property (nonatomic, strong) NSURL *bundleURL;
/**
* The class of the executor currently being used *or* to be used after the next
* reload.
*/
@property (nonatomic, strong) Class executorClass; @property (nonatomic, strong) Class executorClass;
/**
* The delegate provided during the bridge initialization
*/
@property (nonatomic, weak, readonly) id<RCTBridgeDelegate> delegate;
/** /**
* The event dispatcher is a wrapper around -enqueueJSCall:args: that provides a * The event dispatcher is a wrapper around -enqueueJSCall:args: that provides a
* higher-level interface for sending UI events such as touches and text input. * higher-level interface for sending UI events such as touches and text input.

View File

@ -137,6 +137,22 @@ dispatch_queue_t RCTJSThread;
}); });
} }
- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)delegate
launchOptions:(NSDictionary *)launchOptions
{
RCTAssertMainThread();
if ((self = [super init])) {
RCTPerformanceLoggerStart(RCTPLTTI);
_delegate = delegate;
_launchOptions = [launchOptions copy];
[self setUp];
[self bindKeys];
}
return self;
}
- (instancetype)initWithBundleURL:(NSURL *)bundleURL - (instancetype)initWithBundleURL:(NSURL *)bundleURL
moduleProvider:(RCTBridgeModuleProviderBlock)block moduleProvider:(RCTBridgeModuleProviderBlock)block
launchOptions:(NSDictionary *)launchOptions launchOptions:(NSDictionary *)launchOptions

View File

@ -0,0 +1,23 @@
/**
* 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.
*/
typedef void (^RCTSourceLoadBlock)(NSError *error, NSString *source);
@class RCTBridge;
@protocol RCTBridgeDelegate <NSObject>
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge;
@optional
- (NSArray *)extraModulesForBridge:(RCTBridge *)bridge;
- (void)loadSourceForBridge:(RCTBridge *)bridge withBlock:(RCTSourceLoadBlock)loadCallback;
@end

View File

@ -9,7 +9,7 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import "RCTJavaScriptExecutor.h" #import "RCTBridgeDelegate.h"
@class RCTBridge; @class RCTBridge;
@ -20,8 +20,6 @@
*/ */
@interface RCTJavaScriptLoader : NSObject @interface RCTJavaScriptLoader : NSObject
- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER; + (void)loadBundleAtURL:(NSURL *)moduleURL onComplete:(RCTSourceLoadBlock)onComplete;
- (void)loadBundleAtURL:(NSURL *)moduleURL onComplete:(void (^)(NSError *, NSString *))onComplete;
@end @end

View File

@ -15,23 +15,10 @@
#import "RCTUtils.h" #import "RCTUtils.h"
@implementation RCTJavaScriptLoader @implementation RCTJavaScriptLoader
{
__weak RCTBridge *_bridge;
}
- (instancetype)initWithBridge:(RCTBridge *)bridge
{
RCTAssert(bridge, @"bridge parameter is required");
if ((self = [super init])) {
_bridge = bridge;
}
return self;
}
RCT_NOT_IMPLEMENTED(-init) RCT_NOT_IMPLEMENTED(-init)
- (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(void (^)(NSError *, NSString *))onComplete + (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(RCTSourceLoadBlock)onComplete
{ {
// Sanitize the script URL // Sanitize the script URL
scriptURL = [RCTConvert NSURL:scriptURL.absoluteString]; scriptURL = [RCTConvert NSURL:scriptURL.absoluteString];

View File

@ -197,6 +197,7 @@
14435CE41AAC4AE100FC20F4 /* RCTMapManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMapManager.m; sourceTree = "<group>"; }; 14435CE41AAC4AE100FC20F4 /* RCTMapManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMapManager.m; sourceTree = "<group>"; };
146459241B06C49500B389AA /* RCTFPSGraph.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTFPSGraph.h; sourceTree = "<group>"; }; 146459241B06C49500B389AA /* RCTFPSGraph.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTFPSGraph.h; sourceTree = "<group>"; };
146459251B06C49500B389AA /* RCTFPSGraph.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTFPSGraph.m; sourceTree = "<group>"; }; 146459251B06C49500B389AA /* RCTFPSGraph.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTFPSGraph.m; sourceTree = "<group>"; };
1482F9E61B55B927000ADFF3 /* RCTBridgeDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTBridgeDelegate.h; sourceTree = "<group>"; };
14C2CA6F1B3AC63800E6CBB2 /* RCTModuleMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTModuleMethod.h; sourceTree = "<group>"; }; 14C2CA6F1B3AC63800E6CBB2 /* RCTModuleMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTModuleMethod.h; sourceTree = "<group>"; };
14C2CA701B3AC63800E6CBB2 /* RCTModuleMethod.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleMethod.m; sourceTree = "<group>"; }; 14C2CA701B3AC63800E6CBB2 /* RCTModuleMethod.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleMethod.m; sourceTree = "<group>"; };
14C2CA721B3AC64300E6CBB2 /* RCTModuleData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTModuleData.h; sourceTree = "<group>"; }; 14C2CA721B3AC64300E6CBB2 /* RCTModuleData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTModuleData.h; sourceTree = "<group>"; };
@ -482,6 +483,7 @@
1345A83B1B265A0E00583190 /* RCTURLRequestHandler.h */, 1345A83B1B265A0E00583190 /* RCTURLRequestHandler.h */,
83CBBA4F1A601E3B00E9B192 /* RCTUtils.h */, 83CBBA4F1A601E3B00E9B192 /* RCTUtils.h */,
83CBBA501A601E3B00E9B192 /* RCTUtils.m */, 83CBBA501A601E3B00E9B192 /* RCTUtils.m */,
1482F9E61B55B927000ADFF3 /* RCTBridgeDelegate.h */,
); );
path = Base; path = Base;
sourceTree = "<group>"; sourceTree = "<group>";