Skip to content

Commit 8783b49

Browse files
committed
fix: mount UIKit hosts through Worklets
1 parent 100e4fe commit 8783b49

14 files changed

Lines changed: 1354 additions & 324 deletions

packages/react-native/ios/Fabric/NativeScriptUIViewComponentView.mm

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ - (void)updateProps:(Props::Shared const&)props oldProps:(Props::Shared const&)o
6767
const std::string newControllerHandle = newViewProps->controllerHandle;
6868
const std::string oldDebugName = oldViewProps->debugName;
6969
const std::string newDebugName = newViewProps->debugName;
70+
const std::string oldHostId = oldViewProps->hostId;
71+
const std::string newHostId = newViewProps->hostId;
7072

7173
[super updateProps:props oldProps:oldProps];
7274

@@ -100,6 +102,12 @@ - (void)updateProps:(Props::Shared const&)props oldProps:(Props::Shared const&)o
100102
: [NSString stringWithUTF8String:newControllerHandle.c_str()];
101103
_containerView.controllerHandle = controllerHandle;
102104
}
105+
106+
if (oldHostId != newHostId) {
107+
NSString* hostId =
108+
newHostId.empty() ? nil : [NSString stringWithUTF8String:newHostId.c_str()];
109+
_containerView.hostId = hostId;
110+
}
103111
}
104112

105113
- (void)prepareForRecycle {
@@ -110,6 +118,7 @@ - (void)prepareForRecycle {
110118
_containerView.nativeViewHandle = nil;
111119
_containerView.childrenViewHandle = nil;
112120
_containerView.controllerHandle = nil;
121+
_containerView.hostId = nil;
113122
}
114123

115124
+ (ComponentDescriptorProvider)componentDescriptorProvider {

packages/react-native/ios/NativeScriptNativeApiModule.mm

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@
33
#import <Foundation/Foundation.h>
44
#import <TargetConditionals.h>
55

6+
#include <mutex>
67
#include <utility>
78

89
#include "NativeApiJsiReactNative.h"
10+
#include "NativeScriptUIKitHost.h"
911

1012
#include <worklets/Compat/Holders.h>
13+
#include <worklets/WorkletRuntime/WorkletRuntime.h>
1114

1215
namespace {
1316

@@ -94,8 +97,108 @@ bool nativeApiInstalled(facebook::jsi::Runtime& runtime) {
9497
return runtime.global().hasProperty(runtime, "__nativeScriptNativeApi");
9598
}
9699

100+
std::mutex& nativeScriptWorkletRuntimeMutex() {
101+
static std::mutex mutex;
102+
return mutex;
103+
}
104+
105+
std::weak_ptr<worklets::WorkletRuntime>& nativeScriptWorkletRuntime() {
106+
static std::weak_ptr<worklets::WorkletRuntime> runtime;
107+
return runtime;
108+
}
109+
110+
void setNativeScriptWorkletRuntime(
111+
std::shared_ptr<worklets::WorkletRuntime> runtime) {
112+
std::lock_guard<std::mutex> lock(nativeScriptWorkletRuntimeMutex());
113+
nativeScriptWorkletRuntime() = std::move(runtime);
114+
}
115+
116+
std::shared_ptr<worklets::WorkletRuntime> getNativeScriptWorkletRuntime() {
117+
std::lock_guard<std::mutex> lock(nativeScriptWorkletRuntimeMutex());
118+
return nativeScriptWorkletRuntime().lock();
119+
}
120+
121+
NSString* stringProperty(facebook::jsi::Runtime& runtime,
122+
facebook::jsi::Object& object, const char* name) {
123+
auto value = object.getProperty(runtime, name);
124+
if (!value.isString()) {
125+
return nil;
126+
}
127+
std::string text = value.getString(runtime).utf8(runtime);
128+
return [NSString stringWithUTF8String:text.c_str()];
129+
}
130+
97131
} // namespace
98132

133+
NSDictionary<NSString*, NSString*>* NativeScriptCreateUIKitHost(
134+
NSString* hostId) {
135+
if (hostId.length == 0 || ![NSThread isMainThread]) {
136+
return nil;
137+
}
138+
139+
auto workletRuntime = getNativeScriptWorkletRuntime();
140+
if (workletRuntime == nullptr) {
141+
return nil;
142+
}
143+
144+
std::string hostIdString =
145+
hostId.UTF8String != nullptr ? hostId.UTF8String : "";
146+
if (hostIdString.empty()) {
147+
return nil;
148+
}
149+
150+
try {
151+
return workletRuntime->runSync(
152+
[hostIdString = std::move(hostIdString)](
153+
facebook::jsi::Runtime& runtime) -> NSDictionary<NSString*, NSString*>* {
154+
auto global = runtime.global();
155+
auto createValue =
156+
global.getProperty(runtime, "__nativeScriptCreateUIKitHostFromNative");
157+
if (!createValue.isObject()) {
158+
return nil;
159+
}
160+
161+
auto createObject = createValue.asObject(runtime);
162+
if (!createObject.isFunction(runtime)) {
163+
return nil;
164+
}
165+
166+
auto result = createObject.asFunction(runtime).call(
167+
runtime, facebook::jsi::String::createFromUtf8(runtime, hostIdString));
168+
if (!result.isObject()) {
169+
return nil;
170+
}
171+
172+
auto resultObject = result.asObject(runtime);
173+
NSMutableDictionary<NSString*, NSString*>* handles =
174+
[NSMutableDictionary dictionaryWithCapacity:3];
175+
NSString* nativeViewHandle =
176+
stringProperty(runtime, resultObject, "nativeViewHandle");
177+
NSString* childrenViewHandle =
178+
stringProperty(runtime, resultObject, "childrenViewHandle");
179+
NSString* controllerHandle =
180+
stringProperty(runtime, resultObject, "controllerHandle");
181+
182+
if (nativeViewHandle.length > 0) {
183+
handles[@"nativeViewHandle"] = nativeViewHandle;
184+
}
185+
if (childrenViewHandle.length > 0) {
186+
handles[@"childrenViewHandle"] = childrenViewHandle;
187+
}
188+
if (controllerHandle.length > 0) {
189+
handles[@"controllerHandle"] = controllerHandle;
190+
}
191+
return handles;
192+
});
193+
} catch (const std::exception& error) {
194+
NSLog(@"NativeScript failed to create UIKit host %@: %s", hostId,
195+
error.what());
196+
} catch (...) {
197+
NSLog(@"NativeScript failed to create UIKit host %@", hostId);
198+
}
199+
return nil;
200+
}
201+
99202
namespace facebook::react {
100203

101204
NativeScriptNativeApiModule::NativeScriptNativeApiModule(
@@ -134,6 +237,8 @@ bool nativeApiInstalled(facebook::jsi::Runtime& runtime) {
134237
return false;
135238
}
136239

240+
setNativeScriptWorkletRuntime(holder->runtime_);
241+
137242
std::string resolvedMetadataPath =
138243
metadataPath.empty() ? bundledMetadataPath() : metadataPath;
139244
auto jsInvoker = jsInvoker_;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#import <Foundation/Foundation.h>
2+
3+
FOUNDATION_EXPORT NSDictionary<NSString*, NSString*>*
4+
NativeScriptCreateUIKitHost(NSString* hostId);

packages/react-native/ios/NativeScriptUIView.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
@interface NativeScriptUIView : UIView
44

5+
@property(nonatomic, copy) NSString* hostId;
56
@property(nonatomic, copy) NSString* nativeViewHandle;
67
@property(nonatomic, copy) NSString* childrenViewHandle;
78
@property(nonatomic, copy) NSString* controllerHandle;

packages/react-native/ios/NativeScriptUIView.mm

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#import "NativeScriptUIView.h"
2+
#import "NativeScriptUIKitHost.h"
23

34
static id NativeScriptNSObjectFromHandle(NSString* handle) {
45
if (handle == nil || handle.length == 0) {
@@ -64,10 +65,21 @@ - (void)dealloc {
6465
[_nativeViewHandle release];
6566
[_childrenViewHandle release];
6667
[_controllerHandle release];
68+
[_hostId release];
6769
[_debugName release];
6870
[super dealloc];
6971
}
7072

73+
- (void)setHostId:(NSString*)hostId {
74+
if ((_hostId == hostId) || [_hostId isEqualToString:hostId]) {
75+
return;
76+
}
77+
78+
[_hostId release];
79+
_hostId = [hostId copy];
80+
[self mountUIKitHostIfNeeded];
81+
}
82+
7183
- (void)setNativeViewHandle:(NSString*)nativeViewHandle {
7284
if ((_nativeViewHandle == nativeViewHandle) ||
7385
[_nativeViewHandle isEqualToString:nativeViewHandle]) {
@@ -123,6 +135,31 @@ - (NSString*)description {
123135
return [description stringByAppendingFormat:@" debugName = %@", _debugName];
124136
}
125137

138+
- (void)mountUIKitHostIfNeeded {
139+
if (_hostId.length == 0) {
140+
return;
141+
}
142+
143+
NSDictionary<NSString*, NSString*>* handles = NativeScriptCreateUIKitHost(_hostId);
144+
if (handles == nil) {
145+
return;
146+
}
147+
148+
NSString* nativeViewHandle = handles[@"nativeViewHandle"];
149+
NSString* childrenViewHandle = handles[@"childrenViewHandle"];
150+
NSString* controllerHandle = handles[@"controllerHandle"];
151+
152+
if (nativeViewHandle.length > 0) {
153+
self.nativeViewHandle = nativeViewHandle;
154+
}
155+
if (childrenViewHandle.length > 0) {
156+
self.childrenViewHandle = childrenViewHandle;
157+
}
158+
if (controllerHandle.length > 0) {
159+
self.controllerHandle = controllerHandle;
160+
}
161+
}
162+
126163
- (void)setChildrenView:(UIView*)childrenView {
127164
if (_childrenView == childrenView) {
128165
return;
@@ -218,6 +255,7 @@ - (void)insertSubview:(UIView*)view atIndex:(NSInteger)index {
218255

219256
- (void)didMoveToWindow {
220257
[super didMoveToWindow];
258+
[self mountUIKitHostIfNeeded];
221259
[self attachViewControllerIfPossible];
222260
}
223261

packages/react-native/ios/NativeScriptUIViewManager.mm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@ - (UIView*)view {
1717
RCT_EXPORT_VIEW_PROPERTY(childrenViewHandle, NSString)
1818
RCT_EXPORT_VIEW_PROPERTY(controllerHandle, NSString)
1919
RCT_EXPORT_VIEW_PROPERTY(debugName, NSString)
20+
RCT_EXPORT_VIEW_PROPERTY(hostId, NSString)
2021

2122
@end

0 commit comments

Comments
 (0)