[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,49 +14,23 @@
#import "AppDelegate.h"
#import "RCTBridge.h"
#import "RCTJavaScriptLoader.h"
#import "RCTRootView.h"
@interface AppDelegate() <RCTBridgeDelegate>
@end
@implementation AppDelegate
- (BOOL)application:(__unused UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSURL *jsCodeLocation;
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self
launchOptions:launchOptions];
/**
* Loading JavaScript code - uncomment the one you want.
*
* OPTION 1
* Load from development server. Start the server from the repository root:
*
* $ npm start
*
* To run on device, change `localhost` to the IP address of your computer
* (you can get this by typing `ifconfig` into the terminal and selecting the
* `inet` value under `en0:`) and make sure your computer and iOS device are
* on the same Wi-Fi network.
*/
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/UIExplorer/UIExplorerApp.ios.includeRequire.runModule.bundle?dev=true"];
/**
* OPTION 2
* Load from pre-bundled file on disk. To re-generate the static bundle, `cd`
* to your Xcode project folder and run
*
* $ curl 'http://localhost:8081/Examples/UIExplorer/UIExplorerApp.ios.includeRequire.runModule.bundle' -o main.jsbundle
*
* then add the `main.jsbundle` file to your project and uncomment this line:
*/
// jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#if RUNNING_ON_CI
jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"UIExplorerApp"
launchOptions:launchOptions];
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"UIExplorerApp"];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [[UIViewController alloc] init];
@ -66,4 +40,50 @@
return YES;
}
- (NSURL *)sourceURLForBridge:(__unused RCTBridge *)bridge
{
NSURL *sourceURL;
/**
* Loading JavaScript code - uncomment the one you want.
*
* OPTION 1
* Load from development server. Start the server from the repository root:
*
* $ npm start
*
* To run on device, change `localhost` to the IP address of your computer
* (you can get this by typing `ifconfig` into the terminal and selecting the
* `inet` value under `en0:`) and make sure your computer and iOS device are
* on the same Wi-Fi network.
*/
sourceURL = [NSURL URLWithString:@"http://localhost:8081/Examples/UIExplorer/UIExplorerApp.ios.includeRequire.runModule.bundle?dev=true"];
/**
* OPTION 2
* Load from pre-bundled file on disk. To re-generate the static bundle, `cd`
* to your Xcode project folder and run
*
* $ curl 'http://localhost:8081/Examples/UIExplorer/UIExplorerApp.ios.includeRequire.runModule.bundle' -o main.jsbundle
*
* then add the `main.jsbundle` file to your project and uncomment this line:
*/
// sourceURL = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#if RUNNING_ON_CI
sourceURL = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
return sourceURL;
}
- (void)loadSourceForBridge:(RCTBridge *)bridge
withBlock:(RCTSourceLoadBlock)loadCallback
{
[RCTJavaScriptLoader loadBundleAtURL:[self sourceURLForBridge:bridge]
onComplete:loadCallback];
}
@end

View File

@ -149,7 +149,11 @@ RCT_EXPORT_MODULE()
- (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) {
onComplete(error);
}];

View File

@ -156,6 +156,11 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
_parentBridge.bundleURL = bundleURL;
}
- (id<RCTBridgeDelegate>)delegate
{
return _parentBridge.delegate;
}
- (BOOL)isLoading
{
return _loading;
@ -172,7 +177,17 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
// Register passed-in module instances
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;
}
@ -261,7 +276,6 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
}
}];
NSURL *bundleURL = _parentBridge.bundleURL;
if (_javaScriptExecutor == nil) {
/**
@ -270,24 +284,15 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
*/
_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 {
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptWillStartLoadingNotification
object:self
userInfo:@{ @"bridge": self }];
RCTProfileBeginEvent();
RCTPerformanceLoggerStart(RCTPLScriptDownload);
RCTJavaScriptLoader *loader = [[RCTJavaScriptLoader alloc] initWithBridge:self];
[loader loadBundleAtURL:bundleURL onComplete:^(NSError *error, NSString *script) {
RCTProfileBeginEvent();
NSURL *sourceURL = self.delegate ? [self.delegate sourceURLForBridge:_parentBridge] : self.bundleURL;
void (^loadCallback)(NSError *, NSString *) = ^(NSError *error, NSString *script) {
RCTPerformanceLoggerEnd(RCTPLScriptDownload);
RCTProfileEndEvent(@"JavaScript download", @"init,download", @[]);
@ -306,7 +311,7 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
});
RCTSourceCode *sourceCodeModule = self.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])];
sourceCodeModule.scriptURL = bundleURL;
sourceCodeModule.scriptURL = sourceURL;
sourceCodeModule.scriptText = script;
if (error) {
@ -326,7 +331,7 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
} else {
[self enqueueApplicationScript:script url:bundleURL onComplete:^(NSError *loadError) {
[self enqueueApplicationScript:script url:sourceURL onComplete:^(NSError *loadError) {
if (loadError) {
[[RCTRedBox sharedInstance] showError:loadError];
@ -345,7 +350,22 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
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 "RCTBridgeDelegate.h"
#import "RCTBridgeModule.h"
#import "RCTDefines.h"
#import "RCTFrameUpdate.h"
@ -70,7 +71,22 @@ RCT_EXTERN NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass);
*/
@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
* executor. The bridge should then be used for all subsequent communication
* 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;
/**
* The class of the executor currently being used *or* to be used after the next
* reload.
*/
@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
* 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
moduleProvider:(RCTBridgeModuleProviderBlock)block
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 "RCTJavaScriptExecutor.h"
#import "RCTBridgeDelegate.h"
@class RCTBridge;
@ -20,8 +20,6 @@
*/
@interface RCTJavaScriptLoader : NSObject
- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER;
- (void)loadBundleAtURL:(NSURL *)moduleURL onComplete:(void (^)(NSError *, NSString *))onComplete;
+ (void)loadBundleAtURL:(NSURL *)moduleURL onComplete:(RCTSourceLoadBlock)onComplete;
@end

View File

@ -15,23 +15,10 @@
#import "RCTUtils.h"
@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)
- (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(void (^)(NSError *, NSString *))onComplete
+ (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(RCTSourceLoadBlock)onComplete
{
// Sanitize the script URL
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>"; };
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>"; };
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>"; };
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>"; };
@ -482,6 +483,7 @@
1345A83B1B265A0E00583190 /* RCTURLRequestHandler.h */,
83CBBA4F1A601E3B00E9B192 /* RCTUtils.h */,
83CBBA501A601E3B00E9B192 /* RCTUtils.m */,
1482F9E61B55B927000ADFF3 /* RCTBridgeDelegate.h */,
);
path = Base;
sourceTree = "<group>";