Skip to content

Commit

Permalink
Unify sampling profiler and reload logic by leveraging RCTPackagerCon…
Browse files Browse the repository at this point in the history
…nection

Differential Revision: D5951952

fbshipit-source-id: ceea04ebbddb02944717a266a14523be052ab213
  • Loading branch information
fromcelticpark authored and facebook-github-bot committed Oct 5, 2017
1 parent 9e01d72 commit afec987
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 50 deletions.
8 changes: 8 additions & 0 deletions React/Base/RCTDefines.h
Expand Up @@ -56,6 +56,14 @@
#endif
#endif

#ifndef ENABLE_PACKAGER_CONNECTION
#if RCT_DEV && __has_include(<React/RCTPackagerConnection.h>)
#define ENABLE_PACKAGER_CONNECTION 1
#else
#define ENABLE_PACKAGER_CONNECTION 0
#endif
#endif

#if RCT_DEV
#define RCT_IF_DEV(...) __VA_ARGS__
#else
Expand Down
7 changes: 6 additions & 1 deletion React/DevSupport/RCTPackagerClient.h
Expand Up @@ -16,11 +16,16 @@

extern const int RCT_PACKAGER_CLIENT_PROTOCOL_VERSION;

@protocol RCTPackagerClientMethod
@protocol RCTPackagerClientMethod <NSObject>

- (void)handleRequest:(id)params withResponder:(RCTPackagerClientResponder *)responder;
- (void)handleNotification:(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
Expand Down
14 changes: 13 additions & 1 deletion React/DevSupport/RCTPackagerConnection.h
Expand Up @@ -13,19 +13,31 @@

#if RCT_DEV

NS_ASSUME_NONNULL_BEGIN

@class RCTBridge;
@protocol RCTPackagerClientMethod;
@protocol RCTPackagerConnectionConfig;

/**
* Encapsulates connection to React Native packager
* Encapsulates connection to React Native packager.
* Dispatches messages from websocket to message handlers that must implement
* <RCTPackagerClientMethod> protocol.
* Message dispatch is performed on the main queue, unless message handler
* provides its own queue by overriding "methodQueue" method.
*/
@interface RCTPackagerConnection : NSObject

+ (void)checkDefaultConnectionWithCallback:(void (^)(BOOL isRunning))callback
queue:(dispatch_queue_t)queue;

+ (instancetype)connectionForBridge:(RCTBridge *)bridge;
- (instancetype)initWithConfig:(id<RCTPackagerConnectionConfig>)config;
- (void)addHandler:(id<RCTPackagerClientMethod>)handler forMethod:(NSString *)name;
- (void)stop;

@end

NS_ASSUME_NONNULL_END

#endif
91 changes: 71 additions & 20 deletions React/DevSupport/RCTPackagerConnection.m
Expand Up @@ -13,6 +13,7 @@

#import <React/RCTAssert.h>
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTConvert.h>
#import <React/RCTDefines.h>
#import <React/RCTLog.h>
Expand All @@ -26,6 +27,16 @@

#if RCT_DEV

static dispatch_queue_t RCTPackagerConnectionQueue()
{
static dispatch_queue_t queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
queue = dispatch_queue_create("com.facebook.RCTPackagerConnectionQueue", DISPATCH_QUEUE_SERIAL);
});
return queue;
};

@interface RCTPackagerConnection () <RCTWebSocketProtocolDelegate>
@end

Expand All @@ -35,6 +46,29 @@ @implementation RCTPackagerConnection {
NSMutableDictionary<NSString *, id<RCTPackagerClientMethod>> *_handlers;
}

+ (void)checkDefaultConnectionWithCallback:(void (^)(BOOL isRunning))callback
queue:(dispatch_queue_t)queue
{
RCTBundleURLProvider *const settings = [RCTBundleURLProvider sharedSettings];
NSURLComponents *components = [NSURLComponents new];
components.scheme = @"http";
components.host = settings.jsLocation ?: @"localhost";
components.port = @(kRCTBundleURLProviderDefaultPort);
components.path = @"/status";
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:components.URL]
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSString *const status = data != nil
? [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]
: nil;
BOOL isRunning = [status isEqualToString:@"packager-status:running"];

dispatch_async(queue, ^{
callback(isRunning);
});
}];
}

+ (instancetype)connectionForBridge:(RCTBridge *)bridge
{
RCTPackagerConnectionBridgeConfig *config = [[RCTPackagerConnectionBridgeConfig alloc] initWithBridge:bridge];
Expand All @@ -46,7 +80,6 @@ - (instancetype)initWithConfig:(id<RCTPackagerConnectionConfig>)config
if (self = [super init]) {
_packagerURL = [config packagerURL];
_handlers = [[config defaultPackagerMethods] mutableCopy];

[self connect];
}
return self;
Expand All @@ -70,19 +103,35 @@ - (void)connect
}

NSString *key = [url absoluteString];
RCTReconnectingWebSocket *webSocket = socketConnections[key];
if (!webSocket) {
webSocket = [[RCTReconnectingWebSocket alloc] initWithURL:url];
[webSocket start];
socketConnections[key] = webSocket;
_socket = socketConnections[key];
if (!_socket) {
_socket = [[RCTReconnectingWebSocket alloc] initWithURL:url];
_socket.delegateDispatchQueue = RCTPackagerConnectionQueue();
[_socket start];
socketConnections[key] = _socket;
}

webSocket.delegate = self;
_socket.delegate = self;
}

- (void)stop
{
[_socket stop];
}


- (void)addHandler:(id<RCTPackagerClientMethod>)handler forMethod:(NSString *)name
{
_handlers[name] = handler;
@synchronized(self) {
_handlers[name] = handler;
}
}

- (id<RCTPackagerClientMethod>)handlerForMethod:(NSString *)name
{
@synchronized(self) {
return _handlers[name];
}
}

static BOOL isSupportedVersion(NSNumber *version)
Expand All @@ -95,10 +144,6 @@ static BOOL isSupportedVersion(NSNumber *version)

- (void)webSocket:(RCTSRWebSocket *)webSocket didReceiveMessage:(id)message
{
if (!_handlers) {
return;
}

NSError *error = nil;
NSDictionary<NSString *, id> *msg = RCTJSONParse(message, &error);

Expand All @@ -112,7 +157,7 @@ - (void)webSocket:(RCTSRWebSocket *)webSocket didReceiveMessage:(id)message
return;
}

id<RCTPackagerClientMethod> methodHandler = _handlers[msg[@"method"]];
id<RCTPackagerClientMethod> methodHandler = [self handlerForMethod:msg[@"method"]];
if (!methodHandler) {
if (msg[@"id"]) {
NSString *errorMsg = [NSString stringWithFormat:@"%@ no handler found for method %@", [self class], msg[@"method"]];
Expand All @@ -123,13 +168,19 @@ - (void)webSocket:(RCTSRWebSocket *)webSocket didReceiveMessage:(id)message
return; // If it was a broadcast then we ignore it gracefully
}

if (msg[@"id"]) {
[methodHandler handleRequest:msg[@"params"]
withResponder:[[RCTPackagerClientResponder alloc] initWithId:msg[@"id"]
socket:webSocket]];
} else {
[methodHandler handleNotification:msg[@"params"]];
}
dispatch_queue_t methodQueue = [methodHandler respondsToSelector:@selector(methodQueue)]
? [methodHandler methodQueue]
: dispatch_get_main_queue();

dispatch_async(methodQueue, ^{
if (msg[@"id"]) {
[methodHandler handleRequest:msg[@"params"]
withResponder:[[RCTPackagerClientResponder alloc] initWithId:msg[@"id"]
socket:webSocket]];
} else {
[methodHandler handleNotification:msg[@"params"]];
}
});
}

- (void)webSocketDidOpen:(RCTSRWebSocket *)webSocket
Expand Down
4 changes: 4 additions & 0 deletions React/DevSupport/RCTPackagerConnectionBridgeConfig.h
Expand Up @@ -11,6 +11,8 @@

#if RCT_DEV // Only supported in dev mode

NS_ASSUME_NONNULL_BEGIN

@class RCTBridge;

@interface RCTPackagerConnectionBridgeConfig : NSObject <RCTPackagerConnectionConfig>
Expand All @@ -19,4 +21,6 @@

@end

NS_ASSUME_NONNULL_END

#endif
41 changes: 25 additions & 16 deletions React/DevSupport/RCTPackagerConnectionBridgeConfig.m
Expand Up @@ -9,7 +9,10 @@

#import "RCTPackagerConnectionBridgeConfig.h"

#import <objc/runtime.h>

#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>

#import "RCTJSEnvironment.h"
#import "RCTReloadPackagerMethod.h"
Expand All @@ -18,38 +21,44 @@
#if RCT_DEV // Only supported in dev mode

@implementation RCTPackagerConnectionBridgeConfig {
RCTBridge *_bridge;
id<RCTJSEnvironment> _jsEnvironment;
RCTReloadPackagerMethodBlock _reloadCommand;
NSURL *_sourceURL;
}

- (instancetype)initWithBridge:(RCTBridge *)bridge
{
if (self = [super init]) {
_bridge = bridge;
_jsEnvironment = bridge;
_sourceURL = [bridge.bundleURL copy];
__weak RCTBridge *weakBridge = bridge;
_reloadCommand = ^(id params) {
if (params != (id)kCFNull && [params[@"debug"] boolValue]) {
weakBridge.executorClass = objc_lookUpClass("RCTWebSocketExecutor");
}
[weakBridge reload];
};
}
return self;
}

- (NSURL *)packagerURL
{
NSString *host = [_bridge.bundleURL host];
NSString *scheme = [_bridge.bundleURL scheme];
if (!host) {
host = @"localhost";
scheme = @"http";
}

NSNumber *port = [_bridge.bundleURL port];
if (!port) {
port = @8081; // Packager default port
}
return [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@:%@/message?role=ios-rn-rctdevmenu", scheme, host, port]];
NSURLComponents *components = [NSURLComponents new];
NSString *host = [_sourceURL host];
components.host = host ?: @"localhost";
components.scheme = host ? [_sourceURL scheme] : @"http";
components.port = [_sourceURL port] ?: @(kRCTBundleURLProviderDefaultPort);
components.path = @"/message";
components.queryItems = @[[NSURLQueryItem queryItemWithName:@"role" value:@"ios-rn-rctdevmenu"]];
return components.URL;
}

- (NSDictionary<NSString *, id<RCTPackagerClientMethod>> *)defaultPackagerMethods
{
return @{
@"reload": [[RCTReloadPackagerMethod alloc] initWithBridge:_bridge],
@"pokeSamplingProfiler": [[RCTSamplingProfilerPackagerMethod alloc] initWithJSEnvironment:_bridge]
@"reload": [[RCTReloadPackagerMethod alloc] initWithReloadCommand:_reloadCommand callbackQueue:dispatch_get_main_queue()],
@"pokeSamplingProfiler": [[RCTSamplingProfilerPackagerMethod alloc] initWithJSEnvironment:_jsEnvironment]
};
}

Expand Down
4 changes: 4 additions & 0 deletions React/DevSupport/RCTPackagerConnectionConfig.h
Expand Up @@ -11,6 +11,8 @@

#if RCT_DEV // Only supported in dev mode

NS_ASSUME_NONNULL_BEGIN

@protocol RCTPackagerClientMethod;

@protocol RCTPackagerConnectionConfig
Expand All @@ -20,4 +22,6 @@

@end

NS_ASSUME_NONNULL_END

#endif
8 changes: 7 additions & 1 deletion React/DevSupport/RCTReloadPackagerMethod.h
Expand Up @@ -13,10 +13,16 @@

#if RCT_DEV // Only supported in dev mode

NS_ASSUME_NONNULL_BEGIN

typedef void (^RCTReloadPackagerMethodBlock)(id);

@interface RCTReloadPackagerMethod : NSObject <RCTPackagerClientMethod>

- (instancetype)initWithBridge:(RCTBridge *)bridge;
- (instancetype)initWithReloadCommand:(RCTReloadPackagerMethodBlock)block callbackQueue:(dispatch_queue_t)callbackQueue;

@end

NS_ASSUME_NONNULL_END

#endif
20 changes: 11 additions & 9 deletions React/DevSupport/RCTReloadPackagerMethod.m
Expand Up @@ -9,20 +9,20 @@

#import "RCTReloadPackagerMethod.h"

#import <objc/runtime.h>

#import "RCTBridge.h"

#if RCT_DEV // Only supported in dev mode

@implementation RCTReloadPackagerMethod {
__weak RCTBridge *_bridge;
RCTReloadPackagerMethodBlock _block;
dispatch_queue_t _callbackQueue;
}

- (instancetype)initWithBridge:(RCTBridge *)bridge
- (instancetype)initWithReloadCommand:(RCTReloadPackagerMethodBlock)block callbackQueue:(dispatch_queue_t)callbackQueue
{
if (self = [super init]) {
_bridge = bridge;
_block = [block copy];
_callbackQueue = callbackQueue;
}
return self;
}
Expand All @@ -34,10 +34,12 @@ - (void)handleRequest:(__unused id)params withResponder:(RCTPackagerClientRespon

- (void)handleNotification:(id)params
{
if (![params isEqual:[NSNull null]] && [params[@"debug"] boolValue]) {
_bridge.executorClass = objc_lookUpClass("RCTWebSocketExecutor");
}
[_bridge reload];
_block(params);
}

- (dispatch_queue_t)methodQueue
{
return _callbackQueue;
}

@end
Expand Down
2 changes: 0 additions & 2 deletions React/Modules/RCTDevSettings.mm
Expand Up @@ -36,8 +36,6 @@

static NSString *const kRCTDevSettingsUserDefaultsKey = @"RCTDevMenu";

#define ENABLE_PACKAGER_CONNECTION RCT_DEV && __has_include("RCTPackagerConnection.h")

#if ENABLE_PACKAGER_CONNECTION
#import "RCTPackagerConnection.h"
#endif
Expand Down

0 comments on commit afec987

Please sign in to comment.