[RFC][ReactNative] Integrate dev menu directly into RootView

This commit is contained in:
Alex Kotliarskyi 2015-03-25 17:33:54 -07:00
parent 7ffa7942aa
commit c5a6b44c76
7 changed files with 123 additions and 98 deletions

View File

@ -9,7 +9,6 @@
#import "AppDelegate.h" #import "AppDelegate.h"
#import "RCTDevelopmentViewController.h"
#import "RCTRootView.h" #import "RCTRootView.h"
@implementation AppDelegate @implementation AppDelegate
@ -42,7 +41,7 @@
rootView.moduleName = @"UIExplorerApp"; rootView.moduleName = @"UIExplorerApp";
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [[RCTDevelopmentViewController alloc] init]; UIViewController *rootViewController = [[UIViewController alloc] init];
rootViewController.view = rootView; rootViewController.view = rootView;
self.window.rootViewController = rootViewController; self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible]; [self.window makeKeyAndVisible];

View File

@ -9,6 +9,11 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
@interface RCTDevelopmentViewController : UIViewController @class RCTRootView;
@interface RCTDevMenu : NSObject
- (instancetype)initWithRootView:(RCTRootView *)rootView;
- (void)show;
@end @end

View File

@ -0,0 +1,84 @@
/**
* 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.
*/
#import "RCTDevMenu.h"
#import "RCTRedBox.h"
#import "RCTRootView.h"
@interface RCTDevMenu () <UIActionSheetDelegate> {
BOOL _liveReload;
}
@property (nonatomic, weak) RCTRootView *view;
@end
@implementation RCTDevMenu
- (instancetype)initWithRootView:(RCTRootView *)rootView
{
if (self = [super init]) {
self.view = rootView;
}
return self;
}
- (void)show
{
NSString *debugTitle = self.view.executorClass == nil ? @"Enable Debugging" : @"Disable Debugging";
NSString *liveReloadTitle = _liveReload ? @"Disable Live Reload" : @"Enable Live Reload";
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"React Native: Development"
delegate:self
cancelButtonTitle:@"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:@"Reload", debugTitle, liveReloadTitle, nil];
actionSheet.actionSheetStyle = UIBarStyleBlack;
[actionSheet showInView:self.view];
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0) {
[self.view reload];
} else if (buttonIndex == 1) {
self.view.executorClass = self.view.executorClass == nil ? NSClassFromString(@"RCTWebSocketExecutor") : nil;
[self.view reload];
} else if (buttonIndex == 2) {
_liveReload = !_liveReload;
[self _pollAndReload];
}
}
- (void)_pollAndReload
{
if (_liveReload) {
NSURL *url = [self.view scriptURL];
NSURL *longPollURL = [[NSURL alloc] initWithString:@"/onchange" relativeToURL:url];
[self performSelectorInBackground:@selector(_checkForUpdates:) withObject:longPollURL];
}
}
- (void)_checkForUpdates:(NSURL *)URL
{
NSMutableURLRequest *longPollRequest = [NSMutableURLRequest requestWithURL:URL];
longPollRequest.timeoutInterval = 30;
NSHTTPURLResponse *response;
[NSURLConnection sendSynchronousRequest:longPollRequest returningResponse:&response error:nil];
dispatch_async(dispatch_get_main_queue(), ^{
if (_liveReload && response.statusCode == 205) {
[[RCTRedBox sharedInstance] dismiss];
[self.view reload];
}
[self _pollAndReload];
});
}
@end

View File

@ -1,89 +0,0 @@
/**
* 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.
*/
#import "RCTDevelopmentViewController.h"
#import "RCTRedBox.h"
#import "RCTRootView.h"
@interface RCTDevelopmentViewController () <UIActionSheetDelegate> {
BOOL _liveReload;
}
@property (nonatomic, readonly) RCTRootView *RCTView;
@end
@implementation RCTDevelopmentViewController
- (BOOL)canBecomeFirstResponder
{
return YES;
}
- (RCTRootView *)RCTView
{
return (RCTRootView *)self.view;
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake)
{
NSString *debugTitle = self.RCTView.executorClass == nil ? @"Enable Debugging" : @"Disable Debugging";
NSString *liveReloadTitle = _liveReload ? @"Disable Live Reload" : @"Enable Live Reload";
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"React Native: Development"
delegate:self
cancelButtonTitle:@"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:@"Reload", debugTitle, liveReloadTitle, nil];
actionSheet.actionSheetStyle = UIBarStyleBlack;
[actionSheet showInView:self.view];
}
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0) {
[self.RCTView reload];
} else if (buttonIndex == 1) {
self.RCTView.executorClass = self.RCTView.executorClass == nil ? NSClassFromString(@"RCTWebSocketExecutor") : nil;
[self.RCTView reload];
} else if (buttonIndex == 2) {
_liveReload = !_liveReload;
[self _pollAndReload];
}
}
- (void)_pollAndReload
{
if (_liveReload) {
NSURL *url = [self.RCTView scriptURL];
NSURL *longPollURL = [[NSURL alloc] initWithString:@"/onchange" relativeToURL:url];
[self performSelectorInBackground:@selector(_checkForUpdates:) withObject:longPollURL];
}
}
- (void)_checkForUpdates:(NSURL *)URL
{
NSMutableURLRequest *longPollRequest = [NSMutableURLRequest requestWithURL:URL];
longPollRequest.timeoutInterval = 30;
NSHTTPURLResponse *response;
[NSURLConnection sendSynchronousRequest:longPollRequest returningResponse:&response error:nil];
dispatch_async(dispatch_get_main_queue(), ^{
if (_liveReload && response.statusCode == 205) {
[[RCTRedBox sharedInstance] dismiss];
[self.RCTView reload];
}
[self _pollAndReload];
});
}
@end

View File

@ -48,6 +48,12 @@
*/ */
@property (nonatomic, strong) Class executorClass; @property (nonatomic, strong) Class executorClass;
/**
* If YES will watch for shake gestures and show development menu
* with options like "Reload", "Enable Debugging", etc.
*/
@property (nonatomic, assign) BOOL enableDevMenu;
/** /**
* Reload this root view, or all root views, respectively. * Reload this root view, or all root views, respectively.
*/ */

View File

@ -11,6 +11,7 @@
#import "RCTBridge.h" #import "RCTBridge.h"
#import "RCTContextExecutor.h" #import "RCTContextExecutor.h"
#import "RCTDevMenu.h"
#import "RCTEventDispatcher.h" #import "RCTEventDispatcher.h"
#import "RCTKeyCommands.h" #import "RCTKeyCommands.h"
#import "RCTLog.h" #import "RCTLog.h"
@ -26,6 +27,7 @@ NSString *const RCTReloadNotification = @"RCTReloadNotification";
@implementation RCTRootView @implementation RCTRootView
{ {
RCTDevMenu *_devMenu;
RCTBridge *_bridge; RCTBridge *_bridge;
RCTTouchHandler *_touchHandler; RCTTouchHandler *_touchHandler;
id<RCTJavaScriptExecutor> _executor; id<RCTJavaScriptExecutor> _executor;
@ -84,6 +86,9 @@ static Class _globalExecutorClass;
// Numbering of these tags goes from 1, 11, 21, 31, etc // Numbering of these tags goes from 1, 11, 21, 31, etc
static NSInteger rootViewTag = 1; static NSInteger rootViewTag = 1;
self.reactTag = @(rootViewTag); self.reactTag = @(rootViewTag);
#ifdef DEBUG
self.enableDevMenu = YES;
#endif
rootViewTag += 10; rootViewTag += 10;
// Add reload observer // Add reload observer
@ -93,6 +98,21 @@ static Class _globalExecutorClass;
object:nil]; object:nil];
} }
- (BOOL)canBecomeFirstResponder
{
return YES;
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake && self.enableDevMenu) {
if (!_devMenu) {
_devMenu = [[RCTDevMenu alloc] initWithRootView:self];
}
[_devMenu show];
}
}
+ (NSArray *)JSMethods + (NSArray *)JSMethods
{ {
return @[ return @[

View File

@ -8,7 +8,7 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
000E6CEB1AB0E980000CDF4D /* RCTSourceCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 000E6CEA1AB0E980000CDF4D /* RCTSourceCode.m */; }; 000E6CEB1AB0E980000CDF4D /* RCTSourceCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 000E6CEA1AB0E980000CDF4D /* RCTSourceCode.m */; };
00C1A2B31AC0B7E000E89A1C /* RCTDevelopmentViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 00C1A2B21AC0B7E000E89A1C /* RCTDevelopmentViewController.m */; }; 00C1A2B31AC0B7E000E89A1C /* RCTDevMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 00C1A2B21AC0B7E000E89A1C /* RCTDevMenu.m */; };
134FCB361A6D42D900051CC8 /* RCTSparseArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BEE46D1A6D19BC00B5863B /* RCTSparseArray.m */; }; 134FCB361A6D42D900051CC8 /* RCTSparseArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BEE46D1A6D19BC00B5863B /* RCTSparseArray.m */; };
134FCB3D1A6E7F0800051CC8 /* RCTContextExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3A1A6E7F0800051CC8 /* RCTContextExecutor.m */; }; 134FCB3D1A6E7F0800051CC8 /* RCTContextExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3A1A6E7F0800051CC8 /* RCTContextExecutor.m */; };
134FCB3E1A6E7F0800051CC8 /* RCTWebViewExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3C1A6E7F0800051CC8 /* RCTWebViewExecutor.m */; }; 134FCB3E1A6E7F0800051CC8 /* RCTWebViewExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3C1A6E7F0800051CC8 /* RCTWebViewExecutor.m */; };
@ -77,8 +77,8 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
000E6CE91AB0E97F000CDF4D /* RCTSourceCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSourceCode.h; sourceTree = "<group>"; }; 000E6CE91AB0E97F000CDF4D /* RCTSourceCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSourceCode.h; sourceTree = "<group>"; };
000E6CEA1AB0E980000CDF4D /* RCTSourceCode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSourceCode.m; sourceTree = "<group>"; }; 000E6CEA1AB0E980000CDF4D /* RCTSourceCode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSourceCode.m; sourceTree = "<group>"; };
00C1A2B11AC0B7E000E89A1C /* RCTDevelopmentViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDevelopmentViewController.h; sourceTree = "<group>"; }; 00C1A2B11AC0B7E000E89A1C /* RCTDevMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDevMenu.h; sourceTree = "<group>"; };
00C1A2B21AC0B7E000E89A1C /* RCTDevelopmentViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDevelopmentViewController.m; sourceTree = "<group>"; }; 00C1A2B21AC0B7E000E89A1C /* RCTDevMenu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDevMenu.m; sourceTree = "<group>"; };
13442BF21AA90E0B0037E5B0 /* RCTAnimationType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAnimationType.h; sourceTree = "<group>"; }; 13442BF21AA90E0B0037E5B0 /* RCTAnimationType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAnimationType.h; sourceTree = "<group>"; };
13442BF31AA90E0B0037E5B0 /* RCTPointerEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPointerEvents.h; sourceTree = "<group>"; }; 13442BF31AA90E0B0037E5B0 /* RCTPointerEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPointerEvents.h; sourceTree = "<group>"; };
13442BF41AA90E0B0037E5B0 /* RCTViewControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTViewControllerProtocol.h; sourceTree = "<group>"; }; 13442BF41AA90E0B0037E5B0 /* RCTViewControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTViewControllerProtocol.h; sourceTree = "<group>"; };
@ -368,8 +368,8 @@
83CBBA591A601E9000E9B192 /* RCTRedBox.m */, 83CBBA591A601E9000E9B192 /* RCTRedBox.m */,
830A229C1A66C68A008503DA /* RCTRootView.h */, 830A229C1A66C68A008503DA /* RCTRootView.h */,
830A229D1A66C68A008503DA /* RCTRootView.m */, 830A229D1A66C68A008503DA /* RCTRootView.m */,
00C1A2B11AC0B7E000E89A1C /* RCTDevelopmentViewController.h */, 00C1A2B11AC0B7E000E89A1C /* RCTDevMenu.h */,
00C1A2B21AC0B7E000E89A1C /* RCTDevelopmentViewController.m */, 00C1A2B21AC0B7E000E89A1C /* RCTDevMenu.m */,
83BEE46C1A6D19BC00B5863B /* RCTSparseArray.h */, 83BEE46C1A6D19BC00B5863B /* RCTSparseArray.h */,
83BEE46D1A6D19BC00B5863B /* RCTSparseArray.m */, 83BEE46D1A6D19BC00B5863B /* RCTSparseArray.m */,
83CBBA961A6020BB00E9B192 /* RCTTouchHandler.h */, 83CBBA961A6020BB00E9B192 /* RCTTouchHandler.h */,
@ -499,7 +499,7 @@
13B0801A1A69489C00A75B9A /* RCTNavigator.m in Sources */, 13B0801A1A69489C00A75B9A /* RCTNavigator.m in Sources */,
830BA4551A8E3BDA00D53203 /* RCTCache.m in Sources */, 830BA4551A8E3BDA00D53203 /* RCTCache.m in Sources */,
137327E71AA5CF210034F82E /* RCTTabBar.m in Sources */, 137327E71AA5CF210034F82E /* RCTTabBar.m in Sources */,
00C1A2B31AC0B7E000E89A1C /* RCTDevelopmentViewController.m in Sources */, 00C1A2B31AC0B7E000E89A1C /* RCTDevMenu.m in Sources */,
14435CE51AAC4AE100FC20F4 /* RCTMap.m in Sources */, 14435CE51AAC4AE100FC20F4 /* RCTMap.m in Sources */,
134FCB3E1A6E7F0800051CC8 /* RCTWebViewExecutor.m in Sources */, 134FCB3E1A6E7F0800051CC8 /* RCTWebViewExecutor.m in Sources */,
13B0801C1A69489C00A75B9A /* RCTNavItem.m in Sources */, 13B0801C1A69489C00A75B9A /* RCTNavItem.m in Sources */,