initial commit taken from gitlab.lrz.de

This commit is contained in:
privatereese
2018-08-24 18:09:42 +02:00
parent ae54ed4c48
commit fc05486403
28494 changed files with 2159823 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTBridgeModule.h>
@class RCTLoadingProgress;
@interface RCTDevLoadingView : NSObject <RCTBridgeModule>
+ (void)setEnabled:(BOOL)enabled;
- (void)showWithURL:(NSURL *)URL;
- (void)updateProgress:(RCTLoadingProgress *)progress;
- (void)hide;
@end

View File

@@ -0,0 +1,170 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTDevLoadingView.h"
#import <QuartzCore/QuartzCore.h>
#import "RCTBridge.h"
#import "RCTDefines.h"
#import "RCTModalHostViewController.h"
#import "RCTUtils.h"
#if RCT_DEV
static BOOL isEnabled = YES;
@implementation RCTDevLoadingView
{
UIWindow *_window;
UILabel *_label;
NSDate *_showDate;
}
@synthesize bridge = _bridge;
RCT_EXPORT_MODULE()
+ (void)setEnabled:(BOOL)enabled
{
isEnabled = enabled;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
+ (BOOL)requiresMainQueueSetup
{
return YES;
}
- (void)setBridge:(RCTBridge *)bridge
{
_bridge = bridge;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(hide)
name:RCTJavaScriptDidLoadNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(hide)
name:RCTJavaScriptDidFailToLoadNotification
object:nil];
if (bridge.loading) {
[self showWithURL:bridge.bundleURL];
}
}
RCT_EXPORT_METHOD(showMessage:(NSString *)message color:(UIColor *)color backgroundColor:(UIColor *)backgroundColor)
{
if (!isEnabled) {
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
self->_showDate = [NSDate date];
if (!self->_window && !RCTRunningInTestEnvironment()) {
CGSize screenSize = [UIScreen mainScreen].bounds.size;
if (screenSize.height == 812 /* iPhone X */) {
self->_window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, screenSize.width, 60)];
self->_label = [[UILabel alloc] initWithFrame:CGRectMake(0, 30, self->_window.bounds.size.width, 30)];
} else {
self->_window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, screenSize.width, 22)];
self->_label = [[UILabel alloc] initWithFrame:self->_window.bounds];
}
[self->_window addSubview:self->_label];
#if TARGET_OS_TV
self->_window.windowLevel = UIWindowLevelNormal + 1;
#else
self->_window.windowLevel = UIWindowLevelStatusBar + 1;
#endif
// set a root VC so rotation is supported
self->_window.rootViewController = [UIViewController new];
self->_label.font = [UIFont systemFontOfSize:12.0];
self->_label.textAlignment = NSTextAlignmentCenter;
}
self->_label.text = message;
self->_label.textColor = color;
self->_window.backgroundColor = backgroundColor;
self->_window.hidden = NO;
});
}
RCT_EXPORT_METHOD(hide)
{
if (!isEnabled) {
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
const NSTimeInterval MIN_PRESENTED_TIME = 0.6;
NSTimeInterval presentedTime = [[NSDate date] timeIntervalSinceDate:self->_showDate];
NSTimeInterval delay = MAX(0, MIN_PRESENTED_TIME - presentedTime);
CGRect windowFrame = self->_window.frame;
[UIView animateWithDuration:0.25
delay:delay
options:0
animations:^{
self->_window.frame = CGRectOffset(windowFrame, 0, -windowFrame.size.height);
} completion:^(__unused BOOL finished) {
self->_window.frame = windowFrame;
self->_window.hidden = YES;
self->_window = nil;
}];
});
}
- (void)showWithURL:(NSURL *)URL
{
UIColor *color;
UIColor *backgroundColor;
NSString *source;
if (URL.fileURL) {
color = [UIColor grayColor];
backgroundColor = [UIColor blackColor];
source = @"pre-bundled file";
} else {
color = [UIColor whiteColor];
backgroundColor = [UIColor colorWithHue:1./3 saturation:1 brightness:.35 alpha:1];
source = [NSString stringWithFormat:@"%@:%@", URL.host, URL.port];
}
[self showMessage:[NSString stringWithFormat:@"Loading from %@...", source]
color:color
backgroundColor:backgroundColor];
}
- (void)updateProgress:(RCTLoadingProgress *)progress
{
if (!progress) {
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
self->_label.text = [progress description];
});
}
@end
#else
@implementation RCTDevLoadingView
+ (NSString *)moduleName { return nil; }
+ (void)setEnabled:(BOOL)enabled { }
- (void)showWithURL:(NSURL *)URL { }
- (void)updateProgress:(RCTLoadingProgress *)progress { }
- (void)hide { }
@end
#endif

113
node_modules/react-native/React/DevSupport/RCTDevMenu.h generated vendored Normal file
View File

@@ -0,0 +1,113 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import <React/RCTBridge.h>
#import <React/RCTBridgeModule.h>
#if RCT_DEV
RCT_EXTERN NSString *const RCTShowDevMenuNotification;
#endif
@class RCTDevMenuItem;
/**
* Developer menu, useful for exposing extra functionality when debugging.
*/
@interface RCTDevMenu : NSObject
/**
* Deprecated, use RCTDevSettings instead.
*/
@property (nonatomic, assign) BOOL shakeToShow DEPRECATED_ATTRIBUTE;
/**
* Deprecated, use RCTDevSettings instead.
*/
@property (nonatomic, assign) BOOL profilingEnabled DEPRECATED_ATTRIBUTE;
/**
* Deprecated, use RCTDevSettings instead.
*/
@property (nonatomic, assign) BOOL liveReloadEnabled DEPRECATED_ATTRIBUTE;
/**
* Deprecated, use RCTDevSettings instead.
*/
@property (nonatomic, assign) BOOL hotLoadingEnabled DEPRECATED_ATTRIBUTE;
/**
* Presented items in development menu
*/
@property (nonatomic, copy, readonly) NSArray<RCTDevMenuItem *> *presentedItems;
/**
* Detect if actions sheet (development menu) is shown
*/
- (BOOL)isActionSheetShown;
/**
* Manually show the dev menu (can be called from JS).
*/
- (void)show;
/**
* Deprecated, use -[RCTBRidge reload] instead.
*/
- (void)reload DEPRECATED_ATTRIBUTE;
/**
* Deprecated. Use the `-addItem:` method instead.
*/
- (void)addItem:(NSString *)title
handler:(void(^)(void))handler DEPRECATED_ATTRIBUTE;
/**
* Add custom item to the development menu. The handler will be called
* when user selects the item.
*/
- (void)addItem:(RCTDevMenuItem *)item;
@end
typedef NSString *(^RCTDevMenuItemTitleBlock)(void);
/**
* Developer menu item, used to expose additional functionality via the menu.
*/
@interface RCTDevMenuItem : NSObject
/**
* This creates an item with a simple push-button interface, used to trigger an
* action.
*/
+ (instancetype)buttonItemWithTitle:(NSString *)title
handler:(dispatch_block_t)handler;
/**
* This creates an item with a simple push-button interface, used to trigger an
* action. getTitleForPresentation is called each time the item is about to be
* presented, and should return the item's title.
*/
+ (instancetype)buttonItemWithTitleBlock:(RCTDevMenuItemTitleBlock)titleBlock
handler:(dispatch_block_t)handler;
@end
/**
* This category makes the developer menu instance available via the
* RCTBridge, which is useful for any class that needs to access the menu.
*/
@interface RCTBridge (RCTDevMenu)
@property (nonatomic, readonly) RCTDevMenu *devMenu;
@end

425
node_modules/react-native/React/DevSupport/RCTDevMenu.m generated vendored Normal file
View File

@@ -0,0 +1,425 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTDevMenu.h"
#import "RCTBridge+Private.h"
#import "RCTDevSettings.h"
#import "RCTKeyCommands.h"
#import "RCTLog.h"
#import "RCTUtils.h"
#if RCT_DEV
#if RCT_ENABLE_INSPECTOR
#import "RCTInspectorDevServerHelper.h"
#endif
NSString *const RCTShowDevMenuNotification = @"RCTShowDevMenuNotification";
@implementation UIWindow (RCTDevMenu)
- (void)RCT_motionEnded:(__unused UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (event.subtype == UIEventSubtypeMotionShake) {
[[NSNotificationCenter defaultCenter] postNotificationName:RCTShowDevMenuNotification object:nil];
}
}
@end
@implementation RCTDevMenuItem
{
RCTDevMenuItemTitleBlock _titleBlock;
dispatch_block_t _handler;
}
- (instancetype)initWithTitleBlock:(RCTDevMenuItemTitleBlock)titleBlock
handler:(dispatch_block_t)handler
{
if ((self = [super init])) {
_titleBlock = [titleBlock copy];
_handler = [handler copy];
}
return self;
}
RCT_NOT_IMPLEMENTED(- (instancetype)init)
+ (instancetype)buttonItemWithTitleBlock:(NSString *(^)(void))titleBlock handler:(dispatch_block_t)handler
{
return [[self alloc] initWithTitleBlock:titleBlock handler:handler];
}
+ (instancetype)buttonItemWithTitle:(NSString *)title
handler:(dispatch_block_t)handler
{
return [[self alloc] initWithTitleBlock:^NSString *{ return title; } handler:handler];
}
- (void)callHandler
{
if (_handler) {
_handler();
}
}
- (NSString *)title
{
if (_titleBlock) {
return _titleBlock();
}
return nil;
}
@end
typedef void(^RCTDevMenuAlertActionHandler)(UIAlertAction *action);
@interface RCTDevMenu () <RCTBridgeModule, RCTInvalidating>
@end
@implementation RCTDevMenu
{
UIAlertController *_actionSheet;
NSMutableArray<RCTDevMenuItem *> *_extraMenuItems;
}
@synthesize bridge = _bridge;
RCT_EXPORT_MODULE()
+ (void)initialize
{
// We're swizzling here because it's poor form to override methods in a category,
// however UIWindow doesn't actually implement motionEnded:withEvent:, so there's
// no need to call the original implementation.
RCTSwapInstanceMethods([UIWindow class], @selector(motionEnded:withEvent:), @selector(RCT_motionEnded:withEvent:));
}
+ (BOOL)requiresMainQueueSetup
{
return YES;
}
- (instancetype)init
{
if ((self = [super init])) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(showOnShake)
name:RCTShowDevMenuNotification
object:nil];
_extraMenuItems = [NSMutableArray new];
#if TARGET_OS_SIMULATOR
RCTKeyCommands *commands = [RCTKeyCommands sharedInstance];
__weak __typeof(self) weakSelf = self;
// Toggle debug menu
[commands registerKeyCommandWithInput:@"d"
modifierFlags:UIKeyModifierCommand
action:^(__unused UIKeyCommand *command) {
[weakSelf toggle];
}];
// Toggle element inspector
[commands registerKeyCommandWithInput:@"i"
modifierFlags:UIKeyModifierCommand
action:^(__unused UIKeyCommand *command) {
[weakSelf.bridge.devSettings toggleElementInspector];
}];
// Reload in normal mode
[commands registerKeyCommandWithInput:@"n"
modifierFlags:UIKeyModifierCommand
action:^(__unused UIKeyCommand *command) {
[weakSelf.bridge.devSettings setIsDebuggingRemotely:NO];
}];
#endif
}
return self;
}
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
- (void)invalidate
{
_presentedItems = nil;
[_actionSheet dismissViewControllerAnimated:YES completion:^(void){}];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)showOnShake
{
if ([_bridge.devSettings isShakeToShowDevMenuEnabled]) {
[self show];
}
}
- (void)toggle
{
if (_actionSheet) {
[_actionSheet dismissViewControllerAnimated:YES completion:^(void){}];
_actionSheet = nil;
} else {
[self show];
}
}
- (BOOL)isActionSheetShown
{
return _actionSheet != nil;
}
- (void)addItem:(NSString *)title handler:(void(^)(void))handler
{
[self addItem:[RCTDevMenuItem buttonItemWithTitle:title handler:handler]];
}
- (void)addItem:(RCTDevMenuItem *)item
{
[_extraMenuItems addObject:item];
}
- (NSArray<RCTDevMenuItem *> *)_menuItemsToPresent
{
NSMutableArray<RCTDevMenuItem *> *items = [NSMutableArray new];
// Add built-in items
__weak RCTBridge *bridge = _bridge;
__weak RCTDevSettings *devSettings = _bridge.devSettings;
[items addObject:[RCTDevMenuItem buttonItemWithTitle:@"Reload" handler:^{
[bridge reload];
}]];
if (devSettings.isNuclideDebuggingAvailable) {
[items addObject:[RCTDevMenuItem buttonItemWithTitle:[NSString stringWithFormat:@"Debug JS in Nuclide %@", @"\U0001F4AF"] handler:^{
#if RCT_ENABLE_INSPECTOR
[RCTInspectorDevServerHelper attachDebugger:@"ReactNative" withBundleURL:bridge.bundleURL withView: RCTPresentedViewController()];
#endif
}]];
}
if (!devSettings.isRemoteDebuggingAvailable) {
[items addObject:[RCTDevMenuItem buttonItemWithTitle:@"Remote JS Debugger Unavailable" handler:^{
UIAlertController *alertController = [UIAlertController
alertControllerWithTitle:@"Remote JS Debugger Unavailable"
message:@"You need to include the RCTWebSocket library to enable remote JS debugging"
preferredStyle:UIAlertControllerStyleAlert];
__weak typeof(alertController) weakAlertController = alertController;
[alertController addAction:
[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){
[weakAlertController dismissViewControllerAnimated:YES completion:nil];
}]];
[RCTPresentedViewController() presentViewController:alertController animated:YES completion:NULL];
}]];
} else {
[items addObject:[RCTDevMenuItem buttonItemWithTitleBlock:^NSString *{
NSString *title = devSettings.isDebuggingRemotely ? @"Stop Remote JS Debugging" : @"Debug JS Remotely";
if (devSettings.isNuclideDebuggingAvailable) {
return [NSString stringWithFormat:@"%@ %@", title, @"\U0001F645"];
} else {
return title;
}
} handler:^{
devSettings.isDebuggingRemotely = !devSettings.isDebuggingRemotely;
}]];
}
if (devSettings.isLiveReloadAvailable) {
[items addObject:[RCTDevMenuItem buttonItemWithTitleBlock:^NSString *{
return devSettings.isLiveReloadEnabled ? @"Disable Live Reload" : @"Enable Live Reload";
} handler:^{
devSettings.isLiveReloadEnabled = !devSettings.isLiveReloadEnabled;
}]];
[items addObject:[RCTDevMenuItem buttonItemWithTitleBlock:^NSString *{
return devSettings.isProfilingEnabled ? @"Stop Systrace" : @"Start Systrace";
} handler:^{
if (devSettings.isDebuggingRemotely) {
UIAlertController *alertController = [UIAlertController
alertControllerWithTitle:@"Systrace Unavailable"
message:@"You need to stop remote JS debugging to enable Systrace"
preferredStyle:UIAlertControllerStyleAlert];
__weak typeof(alertController) weakAlertController = alertController;
[alertController addAction:
[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){
[weakAlertController dismissViewControllerAnimated:YES completion:nil];
}]];
[RCTPresentedViewController() presentViewController:alertController animated:YES completion:NULL];
} else {
devSettings.isProfilingEnabled = !devSettings.isProfilingEnabled;
}
}]];
}
if (_bridge.devSettings.isHotLoadingAvailable) {
[items addObject:[RCTDevMenuItem buttonItemWithTitleBlock:^NSString *{
return devSettings.isHotLoadingEnabled ? @"Disable Hot Reloading" : @"Enable Hot Reloading";
} handler:^{
devSettings.isHotLoadingEnabled = !devSettings.isHotLoadingEnabled;
}]];
}
if (devSettings.isJSCSamplingProfilerAvailable) {
[items addObject:[RCTDevMenuItem buttonItemWithTitle:@"Start / Stop JS Sampling Profiler" handler:^{
[devSettings toggleJSCSamplingProfiler];
}]];
}
[items addObject:[RCTDevMenuItem buttonItemWithTitleBlock:^NSString *{
return @"Toggle Inspector";
} handler:^{
[devSettings toggleElementInspector];
}]];
[items addObjectsFromArray:_extraMenuItems];
return items;
}
RCT_EXPORT_METHOD(show)
{
if (_actionSheet || !_bridge || RCTRunningInAppExtension()) {
return;
}
NSString *desc = _bridge.bridgeDescription;
if (desc.length == 0) {
desc = NSStringFromClass([_bridge class]);
}
NSString *title = [NSString stringWithFormat:@"React Native: Development (%@)", desc];
// On larger devices we don't have an anchor point for the action sheet
UIAlertControllerStyle style = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone ? UIAlertControllerStyleActionSheet : UIAlertControllerStyleAlert;
_actionSheet = [UIAlertController alertControllerWithTitle:title
message:@""
preferredStyle:style];
NSArray<RCTDevMenuItem *> *items = [self _menuItemsToPresent];
for (RCTDevMenuItem *item in items) {
[_actionSheet addAction:[UIAlertAction actionWithTitle:item.title
style:UIAlertActionStyleDefault
handler:[self alertActionHandlerForDevItem:item]]];
}
[_actionSheet addAction:[UIAlertAction actionWithTitle:@"Cancel"
style:UIAlertActionStyleCancel
handler:[self alertActionHandlerForDevItem:nil]]];
_presentedItems = items;
[RCTPresentedViewController() presentViewController:_actionSheet animated:YES completion:nil];
}
- (RCTDevMenuAlertActionHandler)alertActionHandlerForDevItem:(RCTDevMenuItem *__nullable)item
{
return ^(__unused UIAlertAction *action) {
if (item) {
[item callHandler];
}
self->_actionSheet = nil;
};
}
#pragma mark - deprecated methods and properties
#define WARN_DEPRECATED_DEV_MENU_EXPORT() RCTLogWarn(@"Using deprecated method %s, use RCTDevSettings instead", __func__)
- (void)setShakeToShow:(BOOL)shakeToShow
{
_bridge.devSettings.isShakeToShowDevMenuEnabled = shakeToShow;
}
- (BOOL)shakeToShow
{
return _bridge.devSettings.isShakeToShowDevMenuEnabled;
}
RCT_EXPORT_METHOD(reload)
{
WARN_DEPRECATED_DEV_MENU_EXPORT();
[_bridge reload];
}
RCT_EXPORT_METHOD(debugRemotely:(BOOL)enableDebug)
{
WARN_DEPRECATED_DEV_MENU_EXPORT();
_bridge.devSettings.isDebuggingRemotely = enableDebug;
}
RCT_EXPORT_METHOD(setProfilingEnabled:(BOOL)enabled)
{
WARN_DEPRECATED_DEV_MENU_EXPORT();
_bridge.devSettings.isProfilingEnabled = enabled;
}
- (BOOL)profilingEnabled
{
return _bridge.devSettings.isProfilingEnabled;
}
RCT_EXPORT_METHOD(setLiveReloadEnabled:(BOOL)enabled)
{
WARN_DEPRECATED_DEV_MENU_EXPORT();
_bridge.devSettings.isLiveReloadEnabled = enabled;
}
- (BOOL)liveReloadEnabled
{
return _bridge.devSettings.isLiveReloadEnabled;
}
RCT_EXPORT_METHOD(setHotLoadingEnabled:(BOOL)enabled)
{
WARN_DEPRECATED_DEV_MENU_EXPORT();
_bridge.devSettings.isHotLoadingEnabled = enabled;
}
- (BOOL)hotLoadingEnabled
{
return _bridge.devSettings.isHotLoadingEnabled;
}
@end
#else // Unavailable when not in dev mode
@implementation RCTDevMenu
- (void)show {}
- (void)reload {}
- (void)addItem:(NSString *)title handler:(dispatch_block_t)handler {}
- (void)addItem:(RCTDevMenu *)item {}
- (BOOL)isActionSheetShown { return NO; }
@end
@implementation RCTDevMenuItem
+ (instancetype)buttonItemWithTitle:(NSString *)title handler:(void(^)(void))handler {return nil;}
+ (instancetype)buttonItemWithTitleBlock:(NSString * (^)(void))titleBlock
handler:(void(^)(void))handler {return nil;}
@end
#endif
@implementation RCTBridge (RCTDevMenu)
- (RCTDevMenu *)devMenu
{
#if RCT_DEV
return [self moduleForClass:[RCTDevMenu class]];
#else
return nil;
#endif
}
@end

View File

@@ -0,0 +1,21 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#import <Foundation/Foundation.h>
#import <JavaScriptCore/JSBase.h>
#import <UIKit/UIKit.h>
#import <React/RCTDefines.h>
#import <React/RCTInspectorPackagerConnection.h>
#if RCT_DEV
@interface RCTInspectorDevServerHelper : NSObject
+ (RCTInspectorPackagerConnection *)connectWithBundleURL:(NSURL *)bundleURL;
+ (void)disableDebugger;
+ (void)attachDebugger:(NSString *)owner
withBundleURL:(NSURL *)bundleURL
withView:(UIViewController *)view;
@end
#endif

View File

@@ -0,0 +1,131 @@
#import "RCTInspectorDevServerHelper.h"
#if RCT_DEV
#import <jschelpers/JSCWrapper.h>
#import <UIKit/UIKit.h>
#import <React/RCTLog.h>
#import "RCTDefines.h"
#import "RCTInspectorPackagerConnection.h"
using namespace facebook::react;
static NSString *const kDebuggerMsgDisable = @"{ \"id\":1,\"method\":\"Debugger.disable\" }";
static NSString *getServerHost(NSURL *bundleURL, NSNumber *port)
{
NSString *host = [bundleURL host];
if (!host) {
host = @"localhost";
}
// this is consistent with the Android implementation, where http:// is the
// hardcoded implicit scheme for the debug server. Note, packagerURL
// technically looks like it could handle schemes/protocols other than HTTP,
// so rather than force HTTP, leave it be for now, in case someone is relying
// on that ability when developing against iOS.
return [NSString stringWithFormat:@"%@:%@", host, port];
}
static NSURL *getInspectorDeviceUrl(NSURL *bundleURL)
{
NSNumber *inspectorProxyPort = @8082;
NSString *escapedDeviceName = [[[UIDevice currentDevice] name] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *escapedAppName = [[[NSBundle mainBundle] bundleIdentifier] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@/inspector/device?name=%@&app=%@",
getServerHost(bundleURL, inspectorProxyPort),
escapedDeviceName,
escapedAppName]];
}
static NSURL *getAttachDeviceUrl(NSURL *bundleURL, NSString *title)
{
NSNumber *metroBundlerPort = @8081;
NSString *escapedDeviceName = [[[UIDevice currentDevice] name] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *escapedAppName = [[[NSBundle mainBundle] bundleIdentifier] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@/attach-debugger-nuclide?title=%@&device=%@&app=%@",
getServerHost(bundleURL, metroBundlerPort),
title,
escapedDeviceName,
escapedAppName]];
}
@implementation RCTInspectorDevServerHelper
RCT_NOT_IMPLEMENTED(- (instancetype)init)
static NSMutableDictionary<NSString *, RCTInspectorPackagerConnection *> *socketConnections = nil;
static void sendEventToAllConnections(NSString *event)
{
for (NSString *socketId in socketConnections) {
[socketConnections[socketId] sendEventToAllConnections:event];
}
}
static void displayErrorAlert(UIViewController *view, NSString *message) {
UIAlertController *alert =
[UIAlertController alertControllerWithTitle:nil
message:message
preferredStyle:UIAlertControllerStyleAlert];
[view presentViewController:alert animated:YES completion:nil];
dispatch_after(
dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 2.5),
dispatch_get_main_queue(),
^{
[alert dismissViewControllerAnimated:YES completion:nil];
});
}
+ (void)attachDebugger:(NSString *)owner
withBundleURL:(NSURL *)bundleURL
withView:(UIViewController *)view
{
NSURL *url = getAttachDeviceUrl(bundleURL, owner);
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"GET"];
__weak UIViewController *viewCapture = view;
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:
^(NSData *_Nullable data,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
UIViewController *viewCaptureStrong = viewCapture;
if (error != nullptr && viewCaptureStrong != nullptr) {
displayErrorAlert(viewCaptureStrong, @"The request to attach Nuclide couldn't reach Metro Bundler!");
}
}] resume];
}
+ (void)disableDebugger
{
sendEventToAllConnections(kDebuggerMsgDisable);
}
+ (RCTInspectorPackagerConnection *)connectWithBundleURL:(NSURL *)bundleURL
{
NSURL *inspectorURL = getInspectorDeviceUrl(bundleURL);
// Note, using a static dictionary isn't really the greatest design, but
// the packager connection does the same thing, so it's at least consistent.
// This is a static map that holds different inspector clients per the inspectorURL
if (socketConnections == nil) {
socketConnections = [NSMutableDictionary new];
}
NSString *key = [inspectorURL absoluteString];
RCTInspectorPackagerConnection *connection = socketConnections[key];
if (!connection) {
connection = [[RCTInspectorPackagerConnection alloc] initWithURL:inspectorURL];
socketConnections[key] = connection;
[connection connect];
}
return connection;
}
@end
#endif

View File

@@ -0,0 +1,45 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTDefines.h>
#if RCT_DEV // Only supported in dev mode
@class RCTPackagerClientResponder;
@class RCTReconnectingWebSocket;
#if defined(__cplusplus)
extern "C" {
#endif
extern const int RCT_PACKAGER_CLIENT_PROTOCOL_VERSION;
#if defined(__cplusplus)
}
#endif
@protocol RCTPackagerClientMethod <NSObject>
- (void)handleRequest:(NSDictionary<NSString *, id> *)params withResponder:(RCTPackagerClientResponder *)responder;
- (void)handleNotification:(NSDictionary<NSString *, id> *)params;
@optional
/** By default object will receive its methods on the main queue, unless this method is overriden. */
- (dispatch_queue_t)methodQueue;
@end
@interface RCTPackagerClientResponder : NSObject
- (instancetype)initWithId:(id)msgId socket:(RCTReconnectingWebSocket *)socket;
- (void)respondWithResult:(id)result;
- (void)respondWithError:(id)error;
@end
#endif

View File

@@ -0,0 +1,65 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTPackagerClient.h"
#import <React/RCTLog.h>
#import <React/RCTReconnectingWebSocket.h>
#import <React/RCTUtils.h>
#if RCT_DEV // Only supported in dev mode
const int RCT_PACKAGER_CLIENT_PROTOCOL_VERSION = 2;
@implementation RCTPackagerClientResponder {
id _msgId;
__weak RCTReconnectingWebSocket *_socket;
}
- (instancetype)initWithId:(id)msgId socket:(RCTReconnectingWebSocket *)socket
{
if (self = [super init]) {
_msgId = msgId;
_socket = socket;
}
return self;
}
- (void)respondWithResult:(id)result
{
NSDictionary<NSString *, id> *msg = @{
@"version": @(RCT_PACKAGER_CLIENT_PROTOCOL_VERSION),
@"id": _msgId,
@"result": result,
};
NSError *jsError = nil;
NSString *message = RCTJSONStringify(msg, &jsError);
if (jsError) {
RCTLogError(@"%@ failed to stringify message with error %@", [self class], jsError);
} else {
[_socket send:message];
}
}
- (void)respondWithError:(id)error
{
NSDictionary<NSString *, id> *msg = @{
@"version": @(RCT_PACKAGER_CLIENT_PROTOCOL_VERSION),
@"id": _msgId,
@"error": error,
};
NSError *jsError = nil;
NSString *message = RCTJSONStringify(msg, &jsError);
if (jsError) {
RCTLogError(@"%@ failed to stringify message with error %@", [self class], jsError);
} else {
[_socket send:message];
}
}
@end
#endif

View File

@@ -0,0 +1,76 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
#import <React/RCTDefines.h>
#if RCT_DEV
NS_ASSUME_NONNULL_BEGIN
@protocol RCTPackagerClientMethod;
@class RCTPackagerClientResponder;
typedef uint32_t RCTHandlerToken;
typedef void (^RCTNotificationHandler)(NSDictionary<NSString *, id> *);
typedef void (^RCTRequestHandler)(NSDictionary<NSString *, id> *, RCTPackagerClientResponder *);
typedef void (^RCTConnectedHandler)(void);
/** Encapsulates singleton connection to React Native packager. */
@interface RCTPackagerConnection : NSObject
+ (instancetype)sharedPackagerConnection;
/**
* Registers a handler for a notification broadcast from the packager. An
* example is "reload" - an instruction to reload from the packager.
* If multiple notification handlers are registered for the same method, they
* will all be invoked sequentially.
*/
- (RCTHandlerToken)addNotificationHandler:(RCTNotificationHandler)handler
queue:(dispatch_queue_t)queue
forMethod:(NSString *)method;
/**
* Registers a handler for a request from the packager. An example is
* pokeSamplingProfiler; it asks for profile data from the client.
* Only one handler can be registered for a given method; calling this
* displaces any previous request handler registered for that method.
*/
- (RCTHandlerToken)addRequestHandler:(RCTRequestHandler)handler
queue:(dispatch_queue_t)queue
forMethod:(NSString *)method;
/**
* Registers a handler that runs at most once, when the connection to the
* packager has been established. The handler will be dispatched immediately
* if the connection is already established.
*/
- (RCTHandlerToken)addConnectedHandler:(RCTConnectedHandler)handler
queue:(dispatch_queue_t)queue;
/** Removes a handler. Silently does nothing if the token is not valid. */
- (void)removeHandler:(RCTHandlerToken)token;
/** Disconnects and removes all handlers. */
- (void)stop;
/**
* Historically no distinction was made between notification and request
* handlers. If you use this method, it will be registered as *both* a
* notification handler *and* a request handler. You should migrate to the
* new block-based API instead.
*/
- (void)addHandler:(id<RCTPackagerClientMethod>)handler
forMethod:(NSString *)method __deprecated_msg("Use addRequestHandler or addNotificationHandler instead");
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,283 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTPackagerConnection.h"
#import <algorithm>
#import <objc/runtime.h>
#import <vector>
#import <React/RCTAssert.h>
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTConvert.h>
#import <React/RCTDefines.h>
#import <React/RCTLog.h>
#import <React/RCTPackagerClient.h>
#import <React/RCTReconnectingWebSocket.h>
#import <React/RCTSRWebSocket.h>
#import <React/RCTUtils.h>
#if RCT_DEV
@interface RCTPackagerConnection () <RCTReconnectingWebSocketDelegate>
@end
template <typename Handler>
struct Registration {
NSString *method;
Handler handler;
dispatch_queue_t queue;
uint32_t token;
};
@implementation RCTPackagerConnection {
std::mutex _mutex; // protects all ivars
RCTReconnectingWebSocket *_socket;
BOOL _socketConnected;
NSString *_jsLocationForSocket;
id _bundleURLChangeObserver;
uint32_t _nextToken;
std::vector<Registration<RCTNotificationHandler>> _notificationRegistrations;
std::vector<Registration<RCTRequestHandler>> _requestRegistrations;
std::vector<Registration<RCTConnectedHandler>> _connectedRegistrations;
}
+ (instancetype)sharedPackagerConnection
{
static RCTPackagerConnection *connection;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
connection = [RCTPackagerConnection new];
});
return connection;
}
- (instancetype)init
{
if (self = [super init]) {
_nextToken = 1; // Prevent randomly erasing a handler if you pass a bogus 0 token
_jsLocationForSocket = [RCTBundleURLProvider sharedSettings].jsLocation;
_socket = socketForLocation(_jsLocationForSocket);
_socket.delegate = self;
[_socket start];
RCTPackagerConnection *const __weak weakSelf = self;
_bundleURLChangeObserver =
[[NSNotificationCenter defaultCenter]
addObserverForName:RCTBundleURLProviderUpdatedNotification
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *_Nonnull note) {
[weakSelf bundleURLSettingsChanged];
}];
}
return self;
}
static RCTReconnectingWebSocket *socketForLocation(NSString *const jsLocation)
{
NSURLComponents *const components = [NSURLComponents new];
components.host = jsLocation ?: @"localhost";
components.scheme = @"http";
components.port = @(kRCTBundleURLProviderDefaultPort);
components.path = @"/message";
components.queryItems = @[[NSURLQueryItem queryItemWithName:@"role" value:@"ios"]];
static dispatch_queue_t queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
queue = dispatch_queue_create("com.facebook.RCTPackagerConnectionQueue", DISPATCH_QUEUE_SERIAL);
});
return [[RCTReconnectingWebSocket alloc] initWithURL:components.URL queue:queue];
}
- (void)stop
{
std::lock_guard<std::mutex> l(_mutex);
if (_socket == nil) {
// Already stopped
return;
}
[[NSNotificationCenter defaultCenter] removeObserver:_bundleURLChangeObserver];
_bundleURLChangeObserver = nil;
_socketConnected = NO;
[_socket stop];
_socket = nil;
_notificationRegistrations.clear();
_requestRegistrations.clear();
}
- (void)bundleURLSettingsChanged
{
std::lock_guard<std::mutex> l(_mutex);
if (_socket == nil) {
return; // already stopped
}
NSString *const jsLocation = [RCTBundleURLProvider sharedSettings].jsLocation;
if ([jsLocation isEqual:_jsLocationForSocket]) {
return; // unchanged
}
_socket.delegate = nil;
[_socket stop];
_jsLocationForSocket = jsLocation;
_socket = socketForLocation(jsLocation);
_socket.delegate = self;
[_socket start];
}
- (RCTHandlerToken)addNotificationHandler:(RCTNotificationHandler)handler queue:(dispatch_queue_t)queue forMethod:(NSString *)method
{
std::lock_guard<std::mutex> l(_mutex);
const auto token = _nextToken++;
_notificationRegistrations.push_back({method, handler, queue, token});
return token;
}
- (RCTHandlerToken)addRequestHandler:(RCTRequestHandler)handler queue:(dispatch_queue_t)queue forMethod:(NSString *)method
{
std::lock_guard<std::mutex> l(_mutex);
const auto token = _nextToken++;
_requestRegistrations.push_back({method, handler, queue, token});
return token;
}
- (RCTHandlerToken)addConnectedHandler:(RCTConnectedHandler)handler queue:(dispatch_queue_t)queue
{
std::lock_guard<std::mutex> l(_mutex);
if (_socketConnected) {
dispatch_async(queue, ^{
handler();
});
return 0; // _nextToken starts at 1, so 0 is a no-op token
} else {
const auto token = _nextToken++;
_connectedRegistrations.push_back({nil, handler, queue, token});
return token;
}
}
- (void)removeHandler:(RCTHandlerToken)token
{
std::lock_guard<std::mutex> l(_mutex);
eraseRegistrationsWithToken(_notificationRegistrations, token);
eraseRegistrationsWithToken(_requestRegistrations, token);
eraseRegistrationsWithToken(_connectedRegistrations, token);
}
template <typename Handler>
static void eraseRegistrationsWithToken(std::vector<Registration<Handler>> &registrations, RCTHandlerToken token)
{
registrations.erase(std::remove_if(registrations.begin(), registrations.end(),
[&token](const auto &reg) { return reg.token == token; }),
registrations.end());
}
- (void)addHandler:(id<RCTPackagerClientMethod>)handler forMethod:(NSString *)method
{
dispatch_queue_t queue = [handler respondsToSelector:@selector(methodQueue)]
? [handler methodQueue] : dispatch_get_main_queue();
[self addNotificationHandler:^(NSDictionary<NSString *, id> *notification) {
[handler handleNotification:notification];
} queue:queue forMethod:method];
[self addRequestHandler:^(NSDictionary<NSString *, id> *request, RCTPackagerClientResponder *responder) {
[handler handleRequest:request withResponder:responder];
} queue:queue forMethod:method];
}
static BOOL isSupportedVersion(NSNumber *version)
{
NSArray<NSNumber *> *const kSupportedVersions = @[ @(RCT_PACKAGER_CLIENT_PROTOCOL_VERSION) ];
return [kSupportedVersions containsObject:version];
}
#pragma mark - RCTReconnectingWebSocketDelegate
- (void)reconnectingWebSocketDidOpen:(RCTReconnectingWebSocket *)webSocket
{
std::vector<Registration<RCTConnectedHandler>> registrations;
{
std::lock_guard<std::mutex> l(_mutex);
_socketConnected = YES;
registrations = _connectedRegistrations;
_connectedRegistrations.clear();
}
for (const auto &registration : registrations) {
// Beware: don't capture the reference to handler in a dispatched block!
RCTConnectedHandler handler = registration.handler;
dispatch_async(registration.queue, ^{ handler(); });
}
}
- (void)reconnectingWebSocket:(RCTReconnectingWebSocket *)webSocket didReceiveMessage:(id)message
{
NSError *error = nil;
NSDictionary<NSString *, id> *msg = RCTJSONParse(message, &error);
if (error) {
RCTLogError(@"%@ failed to parse message with error %@\n<message>\n%@\n</message>", [self class], error, msg);
return;
}
if (!isSupportedVersion(msg[@"version"])) {
RCTLogError(@"%@ received message with not supported version %@", [self class], msg[@"version"]);
return;
}
NSString *const method = msg[@"method"];
NSDictionary<NSString *, id> *const params = msg[@"params"];
id messageId = msg[@"id"];
if (messageId) { // Request
const std::vector<Registration<RCTRequestHandler>> registrations(registrationsWithMethod(_mutex, _requestRegistrations, method));
if (registrations.empty()) {
RCTLogError(@"No handler found for packager method %@", msg[@"method"]);
[[[RCTPackagerClientResponder alloc] initWithId:messageId
socket:webSocket]
respondWithError:
[NSString stringWithFormat:@"No handler found for packager method %@", msg[@"method"]]];
} else {
// If there are multiple matching request registrations, only one can win;
// otherwise the packager would get multiple responses. Choose the last one.
RCTRequestHandler handler = registrations.back().handler;
dispatch_async(registrations.back().queue, ^{
handler(params, [[RCTPackagerClientResponder alloc] initWithId:messageId socket:webSocket]);
});
}
} else { // Notification
const std::vector<Registration<RCTNotificationHandler>> registrations(registrationsWithMethod(_mutex, _notificationRegistrations, method));
for (const auto &registration : registrations) {
// Beware: don't capture the reference to handler in a dispatched block!
RCTNotificationHandler handler = registration.handler;
dispatch_async(registration.queue, ^{ handler(params); });
}
}
}
- (void)reconnectingWebSocketDidClose:(RCTReconnectingWebSocket *)webSocket
{
std::lock_guard<std::mutex> l(_mutex);
_socketConnected = NO;
}
template <typename Handler>
static std::vector<Registration<Handler>> registrationsWithMethod(std::mutex &mutex, const std::vector<Registration<Handler>> &registrations, NSString *method)
{
std::lock_guard<std::mutex> l(mutex); // Scope lock acquisition to prevent deadlock when calling out
std::vector<Registration<Handler>> matches;
for (const auto &reg : registrations) {
if ([reg.method isEqual:method]) {
matches.push_back(reg);
}
}
return matches;
}
@end
#endif