RCTCxxInspectorPackagerConnection (#42037)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/42037

Creates an Objective-C wrapper around the C++ version of `InspectorPackagerConnection` (introduced in D52134592), and uses it in React Native iOS apps (behind an internal flag that is off by default).

In future work, the flag will be turned on by default, then deleted, and eventually the legacy `RCTInspectorPackagerConnection` code will be deleted from React Native.

Changelog: [Internal]

Reviewed By: huntie

Differential Revision: D52225495

fbshipit-source-id: f1b9657ef0d665cf7892c15c34c5104e2777ec43
This commit is contained in:
Moti Zilberman
2024-01-09 10:41:13 -08:00
committed by Facebook GitHub Bot
parent a9a736f2ad
commit e28d15fca5
12 changed files with 293 additions and 8 deletions
+1
View File
@@ -104,6 +104,7 @@ Pod::Spec.new do |s|
ss.dependency "React-Core/Default", version
ss.dependency "React-Core/RCTWebSocket", version
ss.dependency "React-jsinspector", version
ss.private_header_files = "React/Inspector/RCTCxx*.h"
end
s.subspec "RCTWebSocket" do |ss|
@@ -15,7 +15,7 @@
@interface RCTInspectorDevServerHelper : NSObject
+ (RCTInspectorPackagerConnection *)connectWithBundleURL:(NSURL *)bundleURL;
+ (id<RCTInspectorPackagerConnectionProtocol>)connectWithBundleURL:(NSURL *)bundleURL;
+ (void)disableDebugger;
+ (void)openDebugger:(NSURL *)bundleURL withErrorMessage:(NSString *)errorMessage;
@end
@@ -12,10 +12,12 @@
#import <React/RCTLog.h>
#import <UIKit/UIKit.h>
#import <React/RCTCxxInspectorPackagerConnection.h>
#import <React/RCTDefines.h>
#import <React/RCTInspectorPackagerConnection.h>
#import <CommonCrypto/CommonCrypto.h>
#import <jsinspector-modern/InspectorFlags.h>
static NSString *const kDebuggerMsgDisable = @"{ \"id\":1,\"method\":\"Debugger.disable\" }";
@@ -107,7 +109,7 @@ static NSURL *getInspectorDeviceUrl(NSURL *bundleURL)
RCT_NOT_IMPLEMENTED(-(instancetype)init)
static NSMutableDictionary<NSString *, RCTInspectorPackagerConnection *> *socketConnections = nil;
static NSMutableDictionary<NSString *, id<RCTInspectorPackagerConnectionProtocol>> *socketConnections = nil;
static void sendEventToAllConnections(NSString *event)
{
@@ -146,7 +148,7 @@ static void sendEventToAllConnections(NSString *event)
sendEventToAllConnections(kDebuggerMsgDisable);
}
+ (RCTInspectorPackagerConnection *)connectWithBundleURL:(NSURL *)bundleURL
+ (id<RCTInspectorPackagerConnectionProtocol>)connectWithBundleURL:(NSURL *)bundleURL
{
NSURL *inspectorURL = getInspectorDeviceUrl(bundleURL);
@@ -158,9 +160,14 @@ static void sendEventToAllConnections(NSString *event)
}
NSString *key = [inspectorURL absoluteString];
RCTInspectorPackagerConnection *connection = socketConnections[key];
id<RCTInspectorPackagerConnectionProtocol> connection = socketConnections[key];
if (!connection || !connection.isConnected) {
connection = [[RCTInspectorPackagerConnection alloc] initWithURL:inspectorURL];
if (facebook::react::jsinspector_modern::InspectorFlags::getInstance().getEnableCxxInspectorPackagerConnection()) {
connection = [[RCTCxxInspectorPackagerConnection alloc] initWithURL:inspectorURL];
} else {
connection = [[RCTInspectorPackagerConnection alloc] initWithURL:inspectorURL];
}
socketConnections[key] = connection;
[connection connect];
}
@@ -0,0 +1,17 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* 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>
#import "RCTInspectorPackagerConnection.h"
#if RCT_DEV || RCT_REMOTE_PROFILE
@interface RCTCxxInspectorPackagerConnection : NSObject <RCTInspectorPackagerConnectionProtocol>
@end
#endif
@@ -0,0 +1,69 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTInspectorPackagerConnection.h>
#if RCT_DEV || RCT_REMOTE_PROFILE
#import <React/RCTDefines.h>
#import <React/RCTInspector.h>
#import <React/RCTLog.h>
#import <React/RCTUtils.h>
#import <SocketRocket/SRWebSocket.h>
#import <jsinspector-modern/InspectorPackagerConnection.h>
#import <chrono>
#import <memory>
#import "RCTCxxInspectorPackagerConnection.h"
#import "RCTCxxInspectorPackagerConnectionDelegate.h"
#import "RCTCxxInspectorWebSocketAdapter.h"
using namespace facebook::react::jsinspector_modern;
@interface RCTCxxInspectorPackagerConnection () {
std::unique_ptr<InspectorPackagerConnection> _cxxImpl;
}
@end
@implementation RCTCxxInspectorPackagerConnection
RCT_NOT_IMPLEMENTED(-(instancetype)init)
- (instancetype)initWithURL:(NSURL *)url
{
if (self = [super init]) {
_cxxImpl = std::make_unique<InspectorPackagerConnection>(
[url absoluteString].UTF8String,
[[NSBundle mainBundle] bundleIdentifier].UTF8String,
std::make_unique<RCTCxxInspectorPackagerConnectionDelegate>());
}
return self;
}
- (void)sendEventToAllConnections:(NSString *)event
{
_cxxImpl->sendEventToAllConnections(event.UTF8String);
}
- (bool)isConnected
{
return _cxxImpl->isConnected();
}
- (void)connect
{
_cxxImpl->connect();
}
- (void)closeQuietly
{
_cxxImpl->closeQuietly();
}
@end
#endif
@@ -0,0 +1,41 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTCxxInspectorWebSocketAdapter.h"
#import <jsinspector-modern/InspectorPackagerConnection.h>
#import <chrono>
#import <memory>
#import <string>
namespace facebook::react::jsinspector_modern {
/**
* Glue between C++ and Objective-C for InspectorPackagerConnectionDelegate.
*/
class RCTCxxInspectorPackagerConnectionDelegate
: public InspectorPackagerConnectionDelegate {
class WebSocket : public IWebSocket {
public:
WebSocket(RCTCxxInspectorWebSocketAdapter* adapter);
virtual void send(std::string_view message) override;
virtual ~WebSocket() override;
private:
RCTCxxInspectorWebSocketAdapter* _adapter;
};
public:
virtual std::unique_ptr<IWebSocket> connectWebSocket(
const std::string& url,
std::weak_ptr<IWebSocketDelegate> delegate) override;
virtual void scheduleCallback(
std::function<void(void)> callback,
std::chrono::milliseconds delayMs) override;
};
} // namespace facebook::react::jsinspector_modern
@@ -0,0 +1,44 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTCxxInspectorPackagerConnectionDelegate.h"
#import <dispatch/dispatch.h>
namespace facebook::react::jsinspector_modern {
RCTCxxInspectorPackagerConnectionDelegate::WebSocket::WebSocket(RCTCxxInspectorWebSocketAdapter *adapter)
: _adapter(adapter)
{
}
void RCTCxxInspectorPackagerConnectionDelegate::WebSocket::send(std::string_view message)
{
[_adapter send:message];
}
RCTCxxInspectorPackagerConnectionDelegate::WebSocket::~WebSocket()
{
[_adapter close];
}
std::unique_ptr<IWebSocket> RCTCxxInspectorPackagerConnectionDelegate::connectWebSocket(
const std::string &url,
std::weak_ptr<IWebSocketDelegate> delegate)
{
auto *adapter = [[RCTCxxInspectorWebSocketAdapter alloc] initWithURL:url delegate:delegate];
return std::make_unique<WebSocket>(adapter);
}
void RCTCxxInspectorPackagerConnectionDelegate::scheduleCallback(
std::function<void(void)> callback,
std::chrono::milliseconds delayMs)
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delayMs.count() * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{
callback();
});
}
} // namespace facebook::react::jsinspector_modern
@@ -0,0 +1,17 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <jsinspector-modern/InspectorPackagerConnection.h>
#import <memory>
#import <string>
@interface RCTCxxInspectorWebSocketAdapter : NSObject
- (instancetype)initWithURL:(const std::string &)url
delegate:(std::weak_ptr<facebook::react::jsinspector_modern::IWebSocketDelegate>)delegate;
- (void)send:(std::string_view)message;
- (void)close;
@end
@@ -0,0 +1,86 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTInspectorPackagerConnection.h>
#import <React/RCTDefines.h>
#import <React/RCTInspector.h>
#import <React/RCTLog.h>
#import <React/RCTUtils.h>
#import <SocketRocket/SRWebSocket.h>
#import <jsinspector-modern/InspectorPackagerConnection.h>
#import <memory>
#import "RCTCxxInspectorWebSocketAdapter.h"
using namespace facebook::react::jsinspector_modern;
namespace {
NSString *NSStringFromUTF8StringView(std::string_view view)
{
return [[NSString alloc] initWithBytes:(const char *)view.data() length:view.size() encoding:NSUTF8StringEncoding];
}
}
@interface RCTCxxInspectorWebSocketAdapter () <SRWebSocketDelegate> {
std::weak_ptr<IWebSocketDelegate> _delegate;
SRWebSocket *_webSocket;
}
@end
@implementation RCTCxxInspectorWebSocketAdapter
- (instancetype)initWithURL:(const std::string &)url delegate:(std::weak_ptr<IWebSocketDelegate>)delegate
{
if ((self = [super init])) {
_delegate = delegate;
_webSocket = [[SRWebSocket alloc] initWithURL:[NSURL URLWithString:NSStringFromUTF8StringView(url)]];
_webSocket.delegate = self;
[_webSocket open];
}
return self;
}
- (void)send:(std::string_view)message
{
__weak RCTCxxInspectorWebSocketAdapter *weakSelf = self;
NSString *messageStr = NSStringFromUTF8StringView(message);
dispatch_async(dispatch_get_main_queue(), ^{
RCTCxxInspectorWebSocketAdapter *strongSelf = weakSelf;
if (strongSelf) {
[strongSelf->_webSocket send:messageStr];
}
});
}
- (void)close
{
[_webSocket closeWithCode:1000 reason:@"End of session"];
}
- (void)webSocket:(__unused SRWebSocket *)webSocket didFailWithError:(NSError *)error
{
if (auto delegate = _delegate.lock()) {
delegate->didFailWithError([error code], [error description].UTF8String);
}
}
- (void)webSocket:(__unused SRWebSocket *)webSocket didReceiveMessageWithString:(NSString *)message
{
if (auto delegate = _delegate.lock()) {
delegate->didReceiveMessage([message UTF8String]);
}
}
- (void)webSocket:(__unused SRWebSocket *)webSocket
didCloseWithCode:(__unused NSInteger)code
reason:(__unused NSString *)reason
wasClean:(__unused BOOL)wasClean
{
if (auto delegate = _delegate.lock()) {
delegate->didClose();
}
}
@end
@@ -9,7 +9,7 @@
#if RCT_DEV || RCT_REMOTE_PROFILE
#include <jsinspector-modern/InspectorInterfaces.h>
#import <jsinspector-modern/InspectorInterfaces.h>
#import <React/RCTDefines.h>
#import <React/RCTInspectorPackagerConnection.h>
@@ -10,7 +10,7 @@
#if RCT_DEV || RCT_REMOTE_PROFILE
@interface RCTInspectorPackagerConnection : NSObject
@protocol RCTInspectorPackagerConnectionProtocol <NSObject>
- (instancetype)initWithURL:(NSURL *)url;
- (bool)isConnected;
@@ -19,6 +19,9 @@
- (void)sendEventToAllConnections:(NSString *)event;
@end
@interface RCTInspectorPackagerConnection : NSObject <RCTInspectorPackagerConnectionProtocol>
@end
@interface RCTInspectorRemoteConnection : NSObject
- (void)onMessage:(NSString *)message;
- (void)onDisconnect;
@@ -30,7 +30,7 @@ Pod::Spec.new do |s|
s.platforms = min_supported_versions
s.source = source
s.source_files = "*.{cpp,h}"
s.header_dir = 'jsinspector'
s.header_dir = 'jsinspector-modern'
s.compiler_flags = folly_compiler_flags
s.pod_target_xcconfig = {
"HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/..\" \"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/fmt/include\"",