Skip to content

Commit

Permalink
Enabling RCTWebSocket on UIKitForMac (macOS Catalyst) (#27469)
Browse files Browse the repository at this point in the history
Summary:
In #25427, radex added initial support for running React Native projects on macOS via Catalyst. However, `RCTWebSocket` was disabled for that target because of some compilation issues. This meant that running projects via a connection to the packager wasn't possible: no live reload, and projects must be run in "Release" mode. It also meant making manual changes to Xcode projects deploying to macOS and scattering a number of conditional checks throughout the codebase.

In this change, I've implemented support for `RCTWebSocket` on the macOS target and re-enabled the affected features. Live reload and the inspector now work for macOS targets. Manual modifications of Xcode build settings are no longer necessary for react-native projects running on macOS.

![Screen Shot 2019-12-10 at 8 36 38 AM](https://user-images.githubusercontent.com/2771/70549905-ce7b0800-1b29-11ea-85c6-07bf09811ae2.png)

### Limitations

There's no binding which displays the developer menu (since there's no shake event on macOS). We'll probably want to add one, perhaps to the menu bar.

I've chosen not to commit the modifications to RNTester which enable macOS support, since that would imply more "official" support for this target than I suspect you all would like to convey. I'm happy to add those chunks if it would be helpful.

## Changelog

[iOS] [Added] - Added web socket support for macOS (Catalyst), enabling debug builds and live reload
Pull Request resolved: #27469

Test Plan:
* Open RNTester/RNTester.xcodeproj with Xcode 11.2.1, run it like a normal iOS app -- make sure it compiles and runs correctly (no regression)
* Select "My Mac" as device target, and run. You may need to configure a valid development team to make signing work.
* RNTester should run fine with no additional configuration. Modify a file in RNTester, note that live reload is now working.
* Test the developer inspector. To display the developer menu, you'll need to manually show it; here's an example diff which does that:
```
 diff --git a/RNTester/js/RNTesterApp.ios.js b/RNTester/js/RNTesterApp.ios.js
index 8245a68d12..a447ad3b1b 100644
 --- a/RNTester/js/RNTesterApp.ios.js
+++ b/RNTester/js/RNTesterApp.ios.js
@@ -19,6 +19,8 @@ const React = require('react');
 const SnapshotViewIOS = require('./examples/Snapshot/SnapshotViewIOS.ios');
 const URIActionMap = require('./utils/URIActionMap');

+import NativeDevMenu from '../../Libraries/NativeModules/specs/NativeDevMenu';
+
 const {
   AppRegistry,
   AsyncStorage,
@@ -143,6 +145,7 @@ class RNTesterApp extends React.Component<Props, RNTesterNavigationState> {

   UNSAFE_componentWillMount() {
     BackHandler.addEventListener('hardwareBackPress', this._handleBack);
+    NativeDevMenu.show();
   }

   componentDidMount() {
```

Reviewed By: sammy-SC

Differential Revision: D18945861

Pulled By: hramos

fbshipit-source-id: edcf02c5803742c89a845a3e5d72bc7dacae839f
  • Loading branch information
andymatuschak authored and facebook-github-bot committed Dec 18, 2019
1 parent 0a525b6 commit f21fa4e
Show file tree
Hide file tree
Showing 13 changed files with 18 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

static NSString *const kErrorUnableToRequestPermissions = @"E_UNABLE_TO_REQUEST_PERMISSIONS";

#if !TARGET_OS_TV && !TARGET_OS_UIKITFORMAC
#if !TARGET_OS_TV
@implementation RCTConvert (NSCalendarUnit)

RCT_ENUM_CONVERTER(NSCalendarUnit,
Expand Down
17 changes: 6 additions & 11 deletions Libraries/WebSocket/RCTSRWebSocket.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,9 @@
// limitations under the License.
//

#if !TARGET_OS_UIKITFORMAC

#import <React/RCTSRWebSocket.h>

#import <Availability.h>
#import <Endian.h>

#import <Security/SecRandom.h>

Expand Down Expand Up @@ -580,7 +577,7 @@ - (void)closeWithCode:(NSInteger)code reason:(NSString *)reason
NSMutableData *mutablePayload = [[NSMutableData alloc] initWithLength:sizeof(uint16_t) + maxMsgSize];
NSData *payload = mutablePayload;

((uint16_t *)mutablePayload.mutableBytes)[0] = EndianU16_BtoN(code);
((uint16_t *)mutablePayload.mutableBytes)[0] = NSSwapBigShortToHost(code);

if (reason) {
NSRange remainingRange = {0};
Expand Down Expand Up @@ -749,7 +746,7 @@ - (void)handleCloseWithData:(NSData *)data
return;
} else if (dataSize >= 2) {
[data getBytes:&closeCode length:sizeof(closeCode)];
_closeCode = EndianU16_BtoN(closeCode);
_closeCode = NSSwapBigShortToHost(closeCode);
if (!closeCodeIsValid(_closeCode)) {
[self _closeWithProtocolError:[NSString stringWithFormat:@"Cannot have close code of %d", _closeCode]];
return;
Expand Down Expand Up @@ -972,12 +969,12 @@ - (void)_readFrameContinue

if (header.payload_length == 126) {
assert(mapped_size >= sizeof(uint16_t));
uint16_t newLen = EndianU16_BtoN(*(uint16_t *)(mapped_buffer));
uint16_t newLen = NSSwapBigShortToHost(*(uint16_t *)(mapped_buffer));
header.payload_length = newLen;
offset += sizeof(uint16_t);
} else if (header.payload_length == 127) {
assert(mapped_size >= sizeof(uint64_t));
header.payload_length = EndianU64_BtoN(*(uint64_t *)(mapped_buffer));
header.payload_length = NSSwapBigLongLongToHost(*(uint64_t *)(mapped_buffer));
offset += sizeof(uint64_t);
} else {
assert(header.payload_length < 126 && header.payload_length >= 0);
Expand Down Expand Up @@ -1264,11 +1261,11 @@ - (void)_sendFrameWithOpcode:(RCTSROpCode)opcode data:(NSData *)data
frame_buffer[1] |= payloadLength;
} else if (payloadLength <= UINT16_MAX) {
frame_buffer[1] |= 126;
*((uint16_t *)(frame_buffer + frame_buffer_size)) = EndianU16_BtoN((uint16_t)payloadLength);
*((uint16_t *)(frame_buffer + frame_buffer_size)) = NSSwapBigShortToHost((uint16_t)payloadLength);
frame_buffer_size += sizeof(uint16_t);
} else {
frame_buffer[1] |= 127;
*((uint64_t *)(frame_buffer + frame_buffer_size)) = EndianU64_BtoN((uint64_t)payloadLength);
*((uint64_t *)(frame_buffer + frame_buffer_size)) = NSSwapBigLongLongToHost((uint64_t)payloadLength);
frame_buffer_size += sizeof(uint64_t);
}

Expand Down Expand Up @@ -1637,5 +1634,3 @@ - (NSRunLoop *)runLoop
}

@end

#endif
2 changes: 1 addition & 1 deletion React/Base/RCTBridge.m
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ - (void)reload
*/
- (void)reloadWithReason:(NSString *)reason
{
#if RCT_ENABLE_INSPECTOR && !TARGET_OS_UIKITFORMAC
#if RCT_ENABLE_INSPECTOR
// Disable debugger to resume the JsVM & avoid thread locks while reloading
[RCTInspectorDevServerHelper disableDebugger];
#endif
Expand Down
2 changes: 1 addition & 1 deletion React/Base/RCTDefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
#endif

#ifndef ENABLE_PACKAGER_CONNECTION
#if RCT_DEV && (__has_include("RCTPackagerConnection.h") || __has_include(<React/RCTPackagerConnection.h>)) && !TARGET_OS_UIKITFORMAC
#if RCT_DEV && (__has_include("RCTPackagerConnection.h") || __has_include(<React/RCTPackagerConnection.h>))
#define ENABLE_PACKAGER_CONNECTION 1
#else
#define ENABLE_PACKAGER_CONNECTION 0
Expand Down
2 changes: 1 addition & 1 deletion React/CoreModules/RCTDevMenu.mm
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ - (void)setDefaultJSBundle
if (devSettings.isNuclideDebuggingAvailable && !devSettings.isDebuggingRemotely) {
[items addObject:[RCTDevMenuItem buttonItemWithTitle:@"Debug with Nuclide"
handler:^{
#if RCT_ENABLE_INSPECTOR && !TARGET_OS_UIKITFORMAC
#if RCT_ENABLE_INSPECTOR
[RCTInspectorDevServerHelper
attachDebugger:@"ReactNative"
withBundleURL:bridge.bundleURL
Expand Down
2 changes: 1 addition & 1 deletion React/CoreModules/RCTDevSettings.mm
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ - (void)setBridge:(RCTBridge *)bridge
forMethod:@"reload"];
#endif

#if RCT_ENABLE_INSPECTOR && !TARGET_OS_UIKITFORMAC
#if RCT_ENABLE_INSPECTOR
// we need this dispatch back to the main thread because even though this
// is executed on the main thread, at this point the bridge is not yet
// finished with its initialisation. But it does finish by the time it
Expand Down
2 changes: 1 addition & 1 deletion React/DevSupport/RCTInspectorDevServerHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#import <React/RCTDefines.h>
#import <React/RCTInspectorPackagerConnection.h>

#if RCT_DEV && !TARGET_OS_UIKITFORMAC
#if RCT_DEV

@interface RCTInspectorDevServerHelper : NSObject

Expand Down
2 changes: 1 addition & 1 deletion React/DevSupport/RCTInspectorDevServerHelper.mm
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

#import <React/RCTInspectorDevServerHelper.h>

#if RCT_DEV && !TARGET_OS_UIKITFORMAC
#if RCT_DEV

#import <React/RCTLog.h>
#import <UIKit/UIKit.h>
Expand Down
2 changes: 1 addition & 1 deletion React/DevSupport/RCTPackagerConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

#import <React/RCTDefines.h>

#if RCT_DEV && !TARGET_OS_UIKITFORMAC
#if RCT_DEV

NS_ASSUME_NONNULL_BEGIN

Expand Down
2 changes: 1 addition & 1 deletion React/DevSupport/RCTPackagerConnection.mm
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
#import <React/RCTReconnectingWebSocket.h>
#import <React/RCTUtils.h>

#if RCT_DEV && !TARGET_OS_UIKITFORMAC
#if RCT_DEV

#import <React/RCTSRWebSocket.h>

Expand Down
2 changes: 1 addition & 1 deletion React/Inspector/RCTInspector.mm
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

#import <React/RCTInspector.h>

#if RCT_DEV && !TARGET_OS_UIKITFORMAC
#if RCT_DEV

#include <jsinspector/InspectorInterfaces.h>

Expand Down
2 changes: 1 addition & 1 deletion React/Inspector/RCTInspectorPackagerConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#import <Foundation/Foundation.h>
#import <React/RCTDefines.h>

#if RCT_DEV && !TARGET_OS_UIKITFORMAC
#if RCT_DEV

@interface RCTBundleStatus : NSObject
@property (atomic, assign) BOOL isLastBundleDownloadSuccess;
Expand Down
2 changes: 1 addition & 1 deletion React/Inspector/RCTInspectorPackagerConnection.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

#import <React/RCTInspectorPackagerConnection.h>

#if RCT_DEV && !TARGET_OS_UIKITFORMAC
#if RCT_DEV

#import <React/RCTDefines.h>
#import <React/RCTInspector.h>
Expand Down

0 comments on commit f21fa4e

Please sign in to comment.