@rnx-kit/react-native-host 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,9 @@
1
+ #import <Foundation/Foundation.h>
2
+
3
+ @class RCTBridge;
4
+
5
+ NS_ASSUME_NONNULL_BEGIN
6
+
7
+ NSObject *RNXInstallSurfacePresenterBridgeAdapter(RCTBridge *bridge);
8
+
9
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,27 @@
1
+ #import "RNXFabricAdapter.h"
2
+
3
+ #ifdef USE_FABRIC
4
+ #pragma clang diagnostic push
5
+ #pragma clang diagnostic ignored "-Wcomma"
6
+ #pragma clang diagnostic ignored "-Wdocumentation"
7
+ #import <React/RCTSurfacePresenter.h>
8
+ #import <React/RCTSurfacePresenterBridgeAdapter.h>
9
+ #import <react/config/ReactNativeConfig.h>
10
+ #pragma clang diagnostic pop
11
+ #endif // USE_FABRIC
12
+
13
+ NSObject *RNXInstallSurfacePresenterBridgeAdapter(RCTBridge *bridge)
14
+ {
15
+ #ifdef USE_FABRIC
16
+ auto contextContainer = std::make_shared<facebook::react::ContextContainer const>();
17
+ auto reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
18
+ contextContainer->insert("ReactNativeConfig", reactNativeConfig);
19
+
20
+ auto bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge
21
+ contextContainer:contextContainer];
22
+ bridge.surfacePresenter = bridgeAdapter.surfacePresenter;
23
+ return bridgeAdapter;
24
+ #else
25
+ return nil;
26
+ #endif // USE_FABRIC
27
+ }
@@ -0,0 +1,41 @@
1
+ #import <Foundation/Foundation.h>
2
+
3
+ #import <React/RCTBridgeDelegate.h>
4
+ #import <React/RCTLog.h>
5
+
6
+ NS_ASSUME_NONNULL_BEGIN
7
+
8
+ /// Configuration object for ``ReactNativeHost``.
9
+ @protocol RNXHostConfig <RCTBridgeDelegate>
10
+
11
+ @optional
12
+
13
+ /// Returns whether the loading view should be visible while loading JavaScript.
14
+ @property (nonatomic, readonly) BOOL isDevLoadingViewEnabled;
15
+
16
+ /// Returns whether the bridge should be released when the app is backgrounded.
17
+ @property (nonatomic, readonly) BOOL shouldReleaseBridgeWhenBackgrounded;
18
+
19
+ /// Logs a message.
20
+ - (void)logWithLevel:(RCTLogLevel)level
21
+ source:(RCTLogSource)source
22
+ filename:(NSString *)filename
23
+ line:(NSNumber *)line
24
+ message:(NSString *)message
25
+ __attribute__((__swift_name__("log(_:source:filename:line:message:)")));
26
+
27
+ // Called when the bridge has been instantiated.
28
+ - (void)onBridgeInstantiated:(RCTBridge *)bridge;
29
+
30
+ // Called when the bridge is about to be shut down.
31
+ - (void)onBridgeWillShutDown:(RCTBridge *)bridge;
32
+
33
+ // Called when the bridge has been shut down.
34
+ - (void)onBridgeDidShutDown;
35
+
36
+ /// Handles a fatal error.
37
+ - (void)onFatalError:(NSError *)error;
38
+
39
+ @end
40
+
41
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,19 @@
1
+ #import <Foundation/Foundation.h>
2
+
3
+ @class RCTBridge;
4
+ @class ReactNativeHost;
5
+
6
+ NS_ASSUME_NONNULL_BEGIN
7
+
8
+ /// Releases resources held by a `ReactNativeHost` instance based on app state.
9
+ @interface RNXHostReleaser : NSObject
10
+
11
+ - (instancetype)init NS_UNAVAILABLE;
12
+
13
+ - (instancetype)initWithHost:(ReactNativeHost *)host NS_DESIGNATED_INITIALIZER;
14
+
15
+ - (void)setBridge:(__weak RCTBridge *)bridge;
16
+
17
+ @end
18
+
19
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,74 @@
1
+ #import "RNXHostReleaser.h"
2
+
3
+ #import <React/RCTBridge.h>
4
+ #import <React/RCTUIManager.h>
5
+ #import <React/RCTUIManagerUtils.h>
6
+
7
+ #import "ReactNativeHost.h"
8
+
9
+ @implementation RNXHostReleaser {
10
+ #if !TARGET_OS_OSX
11
+ __weak ReactNativeHost *_host;
12
+ __weak NSDictionary<NSNumber *, UIView *> *_viewRegistry;
13
+ #endif // !TARGET_OS_OSX
14
+ }
15
+
16
+ - (instancetype)initWithHost:(ReactNativeHost *)host
17
+ {
18
+ if (self = [super init]) {
19
+ #if !TARGET_OS_OSX
20
+ _host = host;
21
+
22
+ NSNotificationCenter *notificationCenter = NSNotificationCenter.defaultCenter;
23
+ [notificationCenter addObserver:self
24
+ selector:@selector(onAppDidEnterBackground:)
25
+ name:UIApplicationDidEnterBackgroundNotification
26
+ object:nil];
27
+ #endif // !TARGET_OS_OSX
28
+ }
29
+ return self;
30
+ }
31
+
32
+ - (void)setBridge:(__weak RCTBridge *)bridge
33
+ {
34
+ #if !TARGET_OS_OSX
35
+ // This may initialize `RCTAccessibilityManager` and must therefore be run
36
+ // on the main queue.
37
+ __weak typeof(self) weakSelf = self;
38
+ RCTExecuteOnMainQueue(^{
39
+ typeof(self) strongSelf = weakSelf;
40
+ if (strongSelf == nil) {
41
+ return;
42
+ }
43
+
44
+ RCTUIManager *manager = bridge.uiManager;
45
+ if (manager == nil) {
46
+ return;
47
+ }
48
+
49
+ // `addUIBlock` must be called on the UIManager queue.
50
+ RCTExecuteOnUIManagerQueue(^{
51
+ [manager addUIBlock:^(RCTUIManager *uiManager,
52
+ NSDictionary<NSNumber *, UIView *> *viewRegistry) {
53
+ typeof(self) strongSelf = weakSelf;
54
+ if (strongSelf == nil) {
55
+ return;
56
+ }
57
+
58
+ strongSelf->_viewRegistry = viewRegistry;
59
+ }];
60
+ });
61
+ });
62
+ #endif // !TARGET_OS_OSX
63
+ }
64
+
65
+ #if !TARGET_OS_OSX
66
+ - (void)onAppDidEnterBackground:(NSNotification *)note
67
+ {
68
+ if (_viewRegistry.count == 0) {
69
+ [_host shutdown];
70
+ }
71
+ }
72
+ #endif // !TARGET_OS_OSX
73
+
74
+ @end
@@ -0,0 +1,29 @@
1
+ #include <memory>
2
+
3
+ #import <Foundation/Foundation.h>
4
+
5
+ #if USE_TURBOMODULE
6
+ #import <ReactCommon/RCTTurboModuleManager.h>
7
+ #endif // USE_TURBOMODULE
8
+
9
+ @class RCTBridge;
10
+
11
+ namespace facebook::react
12
+ {
13
+ class JSExecutorFactory;
14
+ } // namespace facebook::react
15
+
16
+ NS_ASSUME_NONNULL_BEGIN
17
+
18
+ #if USE_TURBOMODULE
19
+ @interface RNXTurboModuleAdapter : NSObject <RCTTurboModuleManagerDelegate>
20
+ #else
21
+ @interface RNXTurboModuleAdapter : NSObject
22
+ #endif // USE_TURBOMODULE
23
+
24
+ - (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:
25
+ (RCTBridge *)bridge;
26
+
27
+ @end
28
+
29
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,84 @@
1
+ #import "RNXTurboModuleAdapter.h"
2
+
3
+ #define FOLLY_NO_CONFIG 1
4
+ #pragma clang diagnostic push
5
+ #pragma clang diagnostic ignored "-Wcomma"
6
+ #import <cxxreact/JSExecutor.h>
7
+ #pragma clang diagnostic pop
8
+
9
+ #if USE_TURBOMODULE
10
+ #import <React/CoreModulesPlugins.h>
11
+ #import <ReactCommon/RCTTurboModuleManager.h>
12
+
13
+ #if __has_include(<React/RCTAppSetupUtils.h>) // <0.72
14
+ #import <React/RCTAppSetupUtils.h>
15
+ #define USE_RUNTIME_SCHEDULER 0
16
+ #else
17
+ #import <RCTAppSetupUtils.h>
18
+
19
+ #import <React/RCTSurfacePresenterBridgeAdapter.h>
20
+ #import <react/renderer/runtimescheduler/RuntimeScheduler.h>
21
+ #import <react/renderer/runtimescheduler/RuntimeSchedulerCallInvoker.h>
22
+ #define USE_RUNTIME_SCHEDULER 1
23
+ #endif // __has_include(<React/RCTAppSetupUtils.h>)
24
+
25
+ #endif // USE_TURBOMODULE
26
+
27
+ @implementation RNXTurboModuleAdapter {
28
+ #if USE_TURBOMODULE
29
+ RCTTurboModuleManager *_turboModuleManager;
30
+ #endif // USE_TURBOMODULE
31
+ #if USE_RUNTIME_SCHEDULER
32
+ std::shared_ptr<facebook::react::RuntimeScheduler> _runtimeScheduler;
33
+ #endif // USE_RUNTIME_SCHEDULER
34
+ }
35
+
36
+ - (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:
37
+ (RCTBridge *)bridge
38
+ {
39
+ #if USE_TURBOMODULE
40
+ // jsExecutorFactoryForBridge: (USE_TURBOMODULE=1)
41
+ #if USE_RUNTIME_SCHEDULER
42
+ _runtimeScheduler =
43
+ std::make_shared<facebook::react::RuntimeScheduler>(RCTRuntimeExecutorFromBridge(bridge));
44
+ auto callInvoker =
45
+ std::make_shared<facebook::react::RuntimeSchedulerCallInvoker>(_runtimeScheduler);
46
+ _turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
47
+ delegate:self
48
+ jsInvoker:callInvoker];
49
+ return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager, _runtimeScheduler);
50
+ #else
51
+ _turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
52
+ delegate:self
53
+ jsInvoker:bridge.jsCallInvoker];
54
+ return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
55
+ #endif // USE_RUNTIME_SCHEDULER
56
+ #else
57
+ // jsExecutorFactoryForBridge: (USE_TURBOMODULE=0)
58
+ return nullptr;
59
+ #endif // USE_TURBOMODULE
60
+ }
61
+
62
+ // MARK: - RCTTurboModuleManagerDelegate details
63
+ #if USE_TURBOMODULE
64
+
65
+ - (Class)getModuleClassFromName:(const char *)name
66
+ {
67
+ return RCTCoreModulesClassProvider(name);
68
+ }
69
+
70
+ - (std::shared_ptr<facebook::react::TurboModule>)
71
+ getTurboModule:(const std::string &)name
72
+ jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
73
+ {
74
+ return nullptr;
75
+ }
76
+
77
+ - (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
78
+ {
79
+ return RCTAppSetupDefaultModuleFromClass(moduleClass);
80
+ }
81
+
82
+ #endif // USE_TURBOMODULE
83
+
84
+ @end
@@ -0,0 +1,44 @@
1
+ #import "ReactNativeHost.h"
2
+
3
+ #ifdef USE_FABRIC
4
+ #import <React/RCTFabricSurfaceHostingProxyRootView.h>
5
+ #else
6
+ #import <React/RCTRootView.h>
7
+ #endif // USE_FABRIC
8
+
9
+ @implementation ReactNativeHost (View)
10
+
11
+ + (instancetype)hostFromRootView:(RNXView *)rootView
12
+ {
13
+ if (![rootView respondsToSelector:@selector(bridge)]) {
14
+ return nil;
15
+ }
16
+
17
+ id bridge = [rootView performSelector:@selector(bridge)];
18
+ if (![bridge respondsToSelector:@selector(delegate)]) {
19
+ return nil;
20
+ }
21
+
22
+ id delegate = [bridge performSelector:@selector(delegate)];
23
+ if (![delegate isKindOfClass:self]) {
24
+ return nil;
25
+ }
26
+
27
+ return delegate;
28
+ }
29
+
30
+ - (RNXView *)viewWithModuleName:(NSString *)moduleName
31
+ initialProperties:(NSDictionary *)initialProperties;
32
+ {
33
+ #ifdef USE_FABRIC
34
+ return [[RCTFabricSurfaceHostingProxyRootView alloc] initWithBridge:self.bridge
35
+ moduleName:moduleName
36
+ initialProperties:initialProperties];
37
+ #else
38
+ return [[RCTRootView alloc] initWithBridge:self.bridge
39
+ moduleName:moduleName
40
+ initialProperties:initialProperties];
41
+ #endif // USE_FABRIC
42
+ }
43
+
44
+ @end
@@ -0,0 +1,56 @@
1
+ #import <Foundation/Foundation.h>
2
+
3
+ #import <React/RCTBridgeDelegate.h>
4
+
5
+ #import "RNXHostConfig.h"
6
+
7
+ #if TARGET_OS_OSX
8
+ @class NSView;
9
+ typedef NSView RNXView;
10
+ #else
11
+ @class UIView;
12
+ typedef UIView RNXView;
13
+ #endif
14
+
15
+ NS_ASSUME_NONNULL_BEGIN
16
+
17
+ /// Hosts a React Native instance.
18
+ @interface ReactNativeHost : NSObject <RCTBridgeDelegate>
19
+
20
+ @property (nonatomic, readonly, nullable) RCTBridge *bridge;
21
+
22
+ - (instancetype)init NS_UNAVAILABLE;
23
+
24
+ - (instancetype)initWithConfig:(id<RNXHostConfig>)config NS_DESIGNATED_INITIALIZER
25
+ NS_SWIFT_NAME(init(_:));
26
+
27
+ - (void)shutdown;
28
+
29
+ /// Calls the specified block when the desired native module is retrieved. Note
30
+ /// that this may initialize the module.
31
+ ///
32
+ /// - Parameters:
33
+ /// - moduleClass: Class of the native module to initialize or retrieve
34
+ /// - block: Block that gets called when the native module is retrieved
35
+ - (void)usingModule:(Class)moduleClass
36
+ block:(void (^)(id<RCTBridgeModule> _Nullable))block
37
+ NS_SWIFT_NAME(using(module:block:));
38
+
39
+ @end
40
+
41
+ @interface ReactNativeHost (View)
42
+
43
+ + (nullable instancetype)hostFromRootView:(RNXView *)rootView NS_SWIFT_NAME(host(from:));
44
+
45
+ /// Creates a React root view with the specified module and initial properties.
46
+ ///
47
+ /// - Parameters:
48
+ /// - moduleName: Name of the module to create root view of
49
+ /// - initialProperties: Properties passed to the module
50
+ - (RNXView *)viewWithModuleName:(NSString *)moduleName
51
+ initialProperties:(nullable NSDictionary *)initialProperties
52
+ NS_SWIFT_NAME(view(moduleName:initialProperties:));
53
+
54
+ @end
55
+
56
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,169 @@
1
+ #import "ReactNativeHost.h"
2
+
3
+ #define FOLLY_NO_CONFIG 1
4
+ #pragma clang diagnostic push
5
+ #pragma clang diagnostic ignored "-Wcomma"
6
+ #import <cxxreact/JSExecutor.h>
7
+ #pragma clang diagnostic pop
8
+
9
+ #import <React/RCTBridge.h>
10
+ #import <React/RCTBundleURLProvider.h>
11
+ #import <React/RCTCxxBridgeDelegate.h>
12
+ #import <React/RCTDevLoadingViewSetEnabled.h>
13
+ #import <React/RCTUtils.h>
14
+
15
+ #import "RNXFabricAdapter.h"
16
+ #import "RNXHostConfig.h"
17
+ #import "RNXHostReleaser.h"
18
+ #import "RNXTurboModuleAdapter.h"
19
+
20
+ @interface ReactNativeHost () <RCTCxxBridgeDelegate>
21
+ @end
22
+
23
+ @implementation ReactNativeHost {
24
+ __weak id<RNXHostConfig> _config;
25
+ RNXTurboModuleAdapter *_turboModuleAdapter;
26
+ NSObject *_surfacePresenterBridgeAdapter;
27
+ RCTBridge *_bridge;
28
+ NSLock *_isShuttingDown;
29
+ RNXHostReleaser *_hostReleaser;
30
+ }
31
+
32
+ - (instancetype)initWithConfig:(id<RNXHostConfig>)config
33
+ {
34
+ if (self = [super init]) {
35
+ if ([config respondsToSelector:@selector(isDevLoadingViewEnabled)]) {
36
+ RCTDevLoadingViewSetEnabled([config isDevLoadingViewEnabled]);
37
+ }
38
+
39
+ if ([config respondsToSelector:@selector(logWithLevel:source:filename:line:message:)]) {
40
+ RCTSetLogFunction(^(RCTLogLevel level,
41
+ RCTLogSource source,
42
+ NSString *filename,
43
+ NSNumber *line,
44
+ NSString *message) {
45
+ [config logWithLevel:level source:source filename:filename line:line message:message];
46
+ });
47
+ }
48
+
49
+ if ([config respondsToSelector:@selector(onFatalError:)]) {
50
+ RCTSetFatalHandler(^(NSError *error) {
51
+ [config onFatalError:error];
52
+ });
53
+ }
54
+
55
+ _config = config;
56
+ #if USE_FABRIC || USE_TURBOMODULE
57
+ _turboModuleAdapter = [[RNXTurboModuleAdapter alloc] init];
58
+ #endif
59
+ _isShuttingDown = [[NSLock alloc] init];
60
+
61
+ if ([config respondsToSelector:@selector(shouldReleaseBridgeWhenBackgrounded)] &&
62
+ [config shouldReleaseBridgeWhenBackgrounded]) {
63
+ _hostReleaser = [[RNXHostReleaser alloc] initWithHost:self];
64
+ }
65
+
66
+ (void)self.bridge; // Initialize the bridge now
67
+ }
68
+ return self;
69
+ }
70
+
71
+ - (RCTBridge *)bridge
72
+ {
73
+ if (![_isShuttingDown tryLock]) {
74
+ NSAssert(NO, @"Tried to access the bridge while shutting down");
75
+ return nil;
76
+ }
77
+
78
+ @try {
79
+ if (_bridge == nil) {
80
+ _bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:nil];
81
+ _surfacePresenterBridgeAdapter = RNXInstallSurfacePresenterBridgeAdapter(_bridge);
82
+ [_hostReleaser setBridge:_bridge];
83
+ if ([_config respondsToSelector:@selector(onBridgeInstantiated:)]) {
84
+ [_config onBridgeInstantiated:_bridge];
85
+ }
86
+ }
87
+
88
+ return _bridge;
89
+ } @finally {
90
+ [_isShuttingDown unlock];
91
+ }
92
+ }
93
+
94
+ - (void)shutdown
95
+ {
96
+ if ([_config respondsToSelector:@selector(onBridgeWillShutDown:)]) {
97
+ [_config onBridgeWillShutDown:_bridge];
98
+ }
99
+
100
+ [_isShuttingDown lock];
101
+
102
+ @try {
103
+ [_bridge invalidate];
104
+ _bridge = nil;
105
+
106
+ if ([_config respondsToSelector:@selector(onBridgeDidShutDown)]) {
107
+ [_config onBridgeDidShutDown];
108
+ }
109
+ } @finally {
110
+ [_isShuttingDown unlock];
111
+ }
112
+ }
113
+
114
+ - (void)usingModule:(Class)moduleClass block:(void (^)(id<RCTBridgeModule> _Nullable))block
115
+ {
116
+ const BOOL requiresMainQueueSetup =
117
+ [moduleClass respondsToSelector:@selector(requiresMainQueueSetup)] &&
118
+ [moduleClass requiresMainQueueSetup];
119
+ if (requiresMainQueueSetup && !RCTIsMainQueue()) {
120
+ __weak id weakSelf = self;
121
+ dispatch_async(dispatch_get_main_queue(), ^{
122
+ [weakSelf usingModule:moduleClass block:block];
123
+ });
124
+ return;
125
+ }
126
+
127
+ id<RCTBridgeModule> bridgeModule = [self.bridge moduleForClass:moduleClass];
128
+ block(bridgeModule);
129
+ }
130
+
131
+ // MARK: - RCTBridgeDelegate details
132
+
133
+ - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
134
+ {
135
+ NSURL *sourceURL = [_config sourceURLForBridge:bridge];
136
+ if (sourceURL != nil) {
137
+ return sourceURL;
138
+ }
139
+
140
+ return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"
141
+ fallbackURLProvider:^NSURL * {
142
+ return nil;
143
+ }];
144
+ }
145
+
146
+ - (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge
147
+ {
148
+ return [_config respondsToSelector:@selector(extraModulesForBridge:)]
149
+ ? [_config extraModulesForBridge:bridge]
150
+ : @[];
151
+ }
152
+
153
+ // MARK: - RCTCxxBridgeDelegate details
154
+
155
+ - (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:
156
+ (RCTBridge *)bridge
157
+ {
158
+ #if USE_TURBOMODULE
159
+ // jsExecutorFactoryForBridge: (USE_TURBOMODULE=1)
160
+ return [_turboModuleAdapter jsExecutorFactoryForBridge:bridge];
161
+ #else
162
+ // jsExecutorFactoryForBridge: (USE_TURBOMODULE=0)
163
+ return nullptr;
164
+ #endif // USE_TURBOMODULE
165
+ }
166
+
167
+ // MARK: - Private
168
+
169
+ @end
@@ -0,0 +1 @@
1
+ /* Dummy file so @react-native-community/cli recognizes this as an iOS package */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rnx-kit/react-native-host",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Simplify React Native initialization",
5
5
  "homepage": "https://github.com/microsoft/rnx-kit/tree/main/packages/react-native-host#readme",
6
6
  "license": "MIT",
@@ -11,7 +11,7 @@
11
11
  "files": [
12
12
  "ReactNativeHost.podspec",
13
13
  "android/",
14
- "mac/"
14
+ "cocoa/"
15
15
  ],
16
16
  "repository": {
17
17
  "type": "git",