expo-modules-core 3.0.18 → 3.0.20
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.
- package/CHANGELOG.md +14 -0
- package/android/build.gradle +2 -2
- package/build/hooks/useReleasingSharedObject.d.ts.map +1 -1
- package/common/cpp/fabric/ExpoViewComponentDescriptor.cpp +28 -0
- package/common/cpp/fabric/ExpoViewState.h +20 -1
- package/ios/AppDelegates/ExpoReactNativeFactoryProtocol.swift +12 -0
- package/ios/Core/Views/SwiftUI/SwiftUIHostingView.swift +20 -8
- package/ios/Core/Views/SwiftUI/SwiftUIShadowNodeProxy.swift +1 -0
- package/ios/Core/Views/SwiftUI/SwiftUIVirtualViewObjC.h +2 -0
- package/ios/Core/Views/SwiftUI/SwiftUIVirtualViewObjC.mm +9 -0
- package/ios/Fabric/ExpoFabricViewObjC.h +2 -0
- package/ios/Fabric/ExpoFabricViewObjC.mm +14 -0
- package/ios/JSI/EXJSIConversions.h +2 -0
- package/ios/JSI/EXJSIConversions.mm +9 -0
- package/ios/ReactDelegates/ExpoReactDelegate.swift +4 -2
- package/ios/Tests/FunctionSpec.swift +67 -4
- package/package.json +2 -2
- package/src/hooks/useReleasingSharedObject.ts +5 -3
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,19 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 3.0.20 — 2025-10-01
|
|
14
|
+
|
|
15
|
+
### 🐛 Bug fixes
|
|
16
|
+
|
|
17
|
+
- [iOS] Fix NSURL to JSIString conversion returning nil. ([#39567](https://github.com/expo/expo/pull/39567) by [@behenate](https://github.com/behenate))
|
|
18
|
+
- Fix SharedObject created with `useReleasingSharedObject` getting destroyed after a fast refresh caused by a change in its dependencies. ([#39753](https://github.com/expo/expo/pull/39753) by [@behenate](https://github.com/behenate))
|
|
19
|
+
|
|
20
|
+
## 3.0.19 — 2025-10-01
|
|
21
|
+
|
|
22
|
+
### 💡 Others
|
|
23
|
+
|
|
24
|
+
- [ios] - Set host dimension synchronously on native ([#40017](https://github.com/expo/expo/pull/40017) by [@nishan](https://github.com/intergalacticspacehighway))
|
|
25
|
+
|
|
13
26
|
## 3.0.18 — 2025-09-22
|
|
14
27
|
|
|
15
28
|
### 🐛 Bug fixes
|
|
@@ -25,6 +38,7 @@ _This version does not introduce any user-facing changes._
|
|
|
25
38
|
### 🎉 New features
|
|
26
39
|
|
|
27
40
|
- [Android] Starts using precompiled headers to improve build times. ([#39641](https://github.com/expo/expo/pull/39641) by [@lukmccall](https://github.com/lukmccall))
|
|
41
|
+
- Remove `ExpoAppDelegate` inheritance requirement in ExpoReactNativeFactory ([#39844](https://github.com/expo/expo/pull/39844) by [@gabrieldonadel](https://github.com/gabrieldonadel))
|
|
28
42
|
|
|
29
43
|
### 🐛 Bug fixes
|
|
30
44
|
|
package/android/build.gradle
CHANGED
|
@@ -29,7 +29,7 @@ if (shouldIncludeCompose) {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
group = 'host.exp.exponent'
|
|
32
|
-
version = '3.0.
|
|
32
|
+
version = '3.0.20'
|
|
33
33
|
|
|
34
34
|
def isExpoModulesCoreTests = {
|
|
35
35
|
Gradle gradle = getGradle()
|
|
@@ -79,7 +79,7 @@ android {
|
|
|
79
79
|
defaultConfig {
|
|
80
80
|
consumerProguardFiles 'proguard-rules.pro'
|
|
81
81
|
versionCode 1
|
|
82
|
-
versionName "3.0.
|
|
82
|
+
versionName "3.0.20"
|
|
83
83
|
buildConfigField "String", "EXPO_MODULES_CORE_VERSION", "\"${versionName}\""
|
|
84
84
|
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled.toString()
|
|
85
85
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useReleasingSharedObject.d.ts","sourceRoot":"","sources":["../../src/hooks/useReleasingSharedObject.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAA8B,MAAM,OAAO,CAAC;AAEnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAEpE;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,aAAa,SAAS,YAAY,EACzE,OAAO,EAAE,MAAM,aAAa,EAC5B,YAAY,EAAE,cAAc,GAC3B,aAAa,
|
|
1
|
+
{"version":3,"file":"useReleasingSharedObject.d.ts","sourceRoot":"","sources":["../../src/hooks/useReleasingSharedObject.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAA8B,MAAM,OAAO,CAAC;AAEnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAEpE;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,aAAa,SAAS,YAAY,EACzE,OAAO,EAAE,MAAM,aAAa,EAC5B,YAAY,EAAE,cAAc,GAC3B,aAAa,CA0Cf"}
|
|
@@ -44,6 +44,34 @@ void ExpoViewComponentDescriptor::adopt(facebook::react::ShadowNode &shadowNode)
|
|
|
44
44
|
|
|
45
45
|
snode->setSize({width, height});
|
|
46
46
|
}
|
|
47
|
+
|
|
48
|
+
// handle layout style prop update
|
|
49
|
+
auto styleWidth = state._styleWidth;
|
|
50
|
+
auto styleHeight = state._styleHeight;
|
|
51
|
+
|
|
52
|
+
if (!isnan(styleWidth) || !isnan(styleHeight)) {
|
|
53
|
+
auto const &props = *std::static_pointer_cast<const facebook::react::ViewProps>(snode->getProps());
|
|
54
|
+
|
|
55
|
+
auto& style = const_cast<facebook::yoga::Style&>(props.yogaStyle);
|
|
56
|
+
bool changedStyle = false;
|
|
57
|
+
|
|
58
|
+
if (!isnan(styleWidth)) {
|
|
59
|
+
style.setDimension(facebook::yoga::Dimension::Width, facebook::yoga::StyleSizeLength::points(styleWidth));
|
|
60
|
+
changedStyle = true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!isnan(styleHeight)) {
|
|
64
|
+
style.setDimension(facebook::yoga::Dimension::Height, facebook::yoga::StyleSizeLength::points(styleHeight));
|
|
65
|
+
changedStyle = true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Update yoga props and dirty layout if we changed the style
|
|
69
|
+
if (changedStyle) {
|
|
70
|
+
auto* expoNode = const_cast<ExpoViewShadowNode*>(snode);
|
|
71
|
+
expoNode->updateYogaProps();
|
|
72
|
+
expoNode->dirtyLayout();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
47
75
|
ConcreteComponentDescriptor::adopt(shadowNode);
|
|
48
76
|
}
|
|
49
77
|
|
|
@@ -28,11 +28,28 @@ public:
|
|
|
28
28
|
_height = std::numeric_limits<float>::quiet_NaN();
|
|
29
29
|
}
|
|
30
30
|
};
|
|
31
|
+
|
|
32
|
+
static ExpoViewState withStyleDimensions(float styleWidth, float styleHeight) {
|
|
33
|
+
ExpoViewState state;
|
|
34
|
+
if (styleWidth >= 0) {
|
|
35
|
+
state._styleWidth = styleWidth;
|
|
36
|
+
} else {
|
|
37
|
+
state._styleWidth = std::numeric_limits<float>::quiet_NaN();
|
|
38
|
+
}
|
|
39
|
+
if (styleHeight >= 0) {
|
|
40
|
+
state._styleHeight = styleHeight;
|
|
41
|
+
} else {
|
|
42
|
+
state._styleHeight = std::numeric_limits<float>::quiet_NaN();
|
|
43
|
+
}
|
|
44
|
+
return state;
|
|
45
|
+
}
|
|
31
46
|
|
|
32
47
|
#ifdef ANDROID
|
|
33
48
|
ExpoViewState(ExpoViewState const &previousState, folly::dynamic data)
|
|
34
49
|
: _width((float)data["width"].getDouble()),
|
|
35
|
-
_height((float)data["height"].getDouble())
|
|
50
|
+
_height((float)data["height"].getDouble()),
|
|
51
|
+
_styleWidth(data.count("styleWidth") ? (float)data["styleWidth"].getDouble() : std::numeric_limits<float>::quiet_NaN()),
|
|
52
|
+
_styleHeight(data.count("styleHeight") ? (float)data["styleHeight"].getDouble() : std::numeric_limits<float>::quiet_NaN()){};
|
|
36
53
|
folly::dynamic getDynamic() const {
|
|
37
54
|
return {};
|
|
38
55
|
};
|
|
@@ -44,6 +61,8 @@ public:
|
|
|
44
61
|
|
|
45
62
|
float _width = std::numeric_limits<float>::quiet_NaN();
|
|
46
63
|
float _height = std::numeric_limits<float>::quiet_NaN();
|
|
64
|
+
float _styleWidth = std::numeric_limits<float>::quiet_NaN();
|
|
65
|
+
float _styleHeight = std::numeric_limits<float>::quiet_NaN();
|
|
47
66
|
|
|
48
67
|
};
|
|
49
68
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
public protocol ExpoReactNativeFactoryProtocol: AnyObject {
|
|
2
|
+
/**
|
|
3
|
+
To decouple RCTAppDelegate dependency from expo-modules-core,
|
|
4
|
+
expo-modules-core doesn't include the concrete `RCTReactNativeFactory` type and let the callsite to include the type
|
|
5
|
+
*/
|
|
6
|
+
func recreateRootView(
|
|
7
|
+
withBundleURL: URL?,
|
|
8
|
+
moduleName: String?,
|
|
9
|
+
initialProps: [AnyHashable: Any]?,
|
|
10
|
+
launchOptions: [AnyHashable: Any]?
|
|
11
|
+
) -> UIView
|
|
12
|
+
}
|
|
@@ -81,6 +81,13 @@ extension ExpoSwiftUI {
|
|
|
81
81
|
self.setViewSize(size)
|
|
82
82
|
#endif
|
|
83
83
|
}
|
|
84
|
+
|
|
85
|
+
shadowNodeProxy.setStyleSize = { width, height in
|
|
86
|
+
#if RCT_NEW_ARCH_ENABLED
|
|
87
|
+
self.setStyleSize(width, height: height)
|
|
88
|
+
#endif
|
|
89
|
+
}
|
|
90
|
+
|
|
84
91
|
shadowNodeProxy.objectWillChange.send()
|
|
85
92
|
|
|
86
93
|
#if os(iOS) || os(tvOS)
|
|
@@ -136,6 +143,12 @@ extension ExpoSwiftUI {
|
|
|
136
143
|
return true
|
|
137
144
|
}
|
|
138
145
|
|
|
146
|
+
public override func layoutSubviews() {
|
|
147
|
+
super.layoutSubviews()
|
|
148
|
+
// TODO: Use updateLayoutMetrics from RN. Add support in ExpoFabricView.
|
|
149
|
+
setupHostingViewConstraints()
|
|
150
|
+
}
|
|
151
|
+
|
|
139
152
|
#if RCT_NEW_ARCH_ENABLED
|
|
140
153
|
/**
|
|
141
154
|
Fabric calls this function when mounting (attaching) a child component view.
|
|
@@ -187,14 +200,13 @@ extension ExpoSwiftUI {
|
|
|
187
200
|
guard let view = hostingController.view as UIView? else {
|
|
188
201
|
return
|
|
189
202
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
view.
|
|
194
|
-
|
|
195
|
-
view.
|
|
196
|
-
|
|
197
|
-
])
|
|
203
|
+
let frame = self.bounds;
|
|
204
|
+
view.frame = frame;
|
|
205
|
+
#if os(iOS) || os(tvOS)
|
|
206
|
+
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
207
|
+
#elseif os(macOS)
|
|
208
|
+
view.autoresizingMask = [.width, .height]
|
|
209
|
+
#endif
|
|
198
210
|
}
|
|
199
211
|
|
|
200
212
|
// MARK: - UIView lifecycle
|
|
@@ -357,6 +357,15 @@ static std::unordered_map<std::string, expo::ExpoViewComponentDescriptor::Flavor
|
|
|
357
357
|
}
|
|
358
358
|
}
|
|
359
359
|
|
|
360
|
+
- (void)setStyleSize:(nullable NSNumber *)width height:(nullable NSNumber *)height
|
|
361
|
+
{
|
|
362
|
+
if (_state) {
|
|
363
|
+
float widthValue = width ? [width floatValue] : std::numeric_limits<float>::quiet_NaN();
|
|
364
|
+
float heightValue = height ? [height floatValue] : std::numeric_limits<float>::quiet_NaN();
|
|
365
|
+
_state->updateState(expo::ExpoViewState::withStyleDimensions(widthValue, heightValue));
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
360
369
|
- (BOOL)supportsPropWithName:(nonnull NSString *)name
|
|
361
370
|
{
|
|
362
371
|
// Implemented in `SwiftUIVirtualView.swift`
|
|
@@ -40,6 +40,8 @@
|
|
|
40
40
|
|
|
41
41
|
- (void)setShadowNodeSize:(float)width height:(float)height;
|
|
42
42
|
|
|
43
|
+
- (void)setStyleSize:(nullable NSNumber *)width height:(nullable NSNumber *)height;
|
|
44
|
+
|
|
43
45
|
- (BOOL)supportsPropWithName:(nonnull NSString *)name;
|
|
44
46
|
|
|
45
47
|
// MARK: - Derived from RCTComponentViewProtocol
|
|
@@ -179,6 +179,20 @@ static std::unordered_map<std::string, ExpoViewComponentDescriptor::Flavor> _com
|
|
|
179
179
|
return NO;
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
+
- (void)setStyleSize:(nullable NSNumber *)width height:(nullable NSNumber *)height
|
|
183
|
+
{
|
|
184
|
+
if (_state) {
|
|
185
|
+
float widthValue = width ? [width floatValue] : std::numeric_limits<float>::quiet_NaN();
|
|
186
|
+
float heightValue = height ? [height floatValue] : std::numeric_limits<float>::quiet_NaN();
|
|
187
|
+
#if REACT_NATIVE_TARGET_VERSION >= 82
|
|
188
|
+
// synchronous update is only available in React Native 0.82 and above
|
|
189
|
+
_state->updateState(expo::ExpoViewState::withStyleDimensions(widthValue, heightValue), EventQueue::UpdateMode::unstable_Immediate);
|
|
190
|
+
#else
|
|
191
|
+
_state->updateState(expo::ExpoViewState::withStyleDimensions(widthValue, heightValue));
|
|
192
|
+
#endif
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
182
196
|
@end
|
|
183
197
|
|
|
184
198
|
#endif // RCT_NEW_ARCH_ENABLED
|
|
@@ -24,6 +24,8 @@ namespace expo
|
|
|
24
24
|
|
|
25
25
|
jsi::String convertNSStringToJSIString(jsi::Runtime &runtime, NSString *value);
|
|
26
26
|
|
|
27
|
+
jsi::String convertNSURLToJSIString(jsi::Runtime &runtime, NSURL *value);
|
|
28
|
+
|
|
27
29
|
jsi::Object convertNSDictionaryToJSIObject(jsi::Runtime &runtime, NSDictionary *value);
|
|
28
30
|
|
|
29
31
|
jsi::Array convertNSArrayToJSIArray(jsi::Runtime &runtime, NSArray *value);
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
#import <ExpoModulesCore/EXJavaScriptRuntime.h>
|
|
10
10
|
#import <ExpoModulesCore/EXJavaScriptSharedObjectBinding.h>
|
|
11
11
|
#import <ExpoModulesCore/EXStringUtils.h>
|
|
12
|
+
#import <Foundation/NSURL.h>
|
|
12
13
|
|
|
13
14
|
namespace expo {
|
|
14
15
|
|
|
@@ -41,6 +42,12 @@ jsi::String convertNSStringToJSIString(jsi::Runtime &runtime, NSString *value)
|
|
|
41
42
|
#endif
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
jsi::String convertNSURLToJSIString(jsi::Runtime &runtime, NSURL *value)
|
|
46
|
+
{
|
|
47
|
+
NSString *stringValue = [value absoluteString];
|
|
48
|
+
return convertNSStringToJSIString(runtime, stringValue);
|
|
49
|
+
}
|
|
50
|
+
|
|
44
51
|
jsi::Object convertNSDictionaryToJSIObject(jsi::Runtime &runtime, NSDictionary *value)
|
|
45
52
|
{
|
|
46
53
|
jsi::Object result = jsi::Object(runtime);
|
|
@@ -107,6 +114,8 @@ jsi::Value convertObjCObjectToJSIValue(jsi::Runtime &runtime, id value)
|
|
|
107
114
|
return convertNSArrayToJSIArray(runtime, (NSArray *)value);
|
|
108
115
|
} else if ([value isKindOfClass:[NSData class]]) {
|
|
109
116
|
return createUint8Array(runtime, (NSData *)value);
|
|
117
|
+
} else if ([value isKindOfClass:[NSURL class]]) {
|
|
118
|
+
return convertNSURLToJSIString(runtime, (NSURL *)value);
|
|
110
119
|
} else if (value == (id)kCFNull) {
|
|
111
120
|
return jsi::Value::null();
|
|
112
121
|
}
|
|
@@ -6,9 +6,11 @@
|
|
|
6
6
|
@objc(EXReactDelegate)
|
|
7
7
|
public class ExpoReactDelegate: NSObject {
|
|
8
8
|
private let handlers: [ExpoReactDelegateHandler]
|
|
9
|
+
public let reactNativeFactory: ExpoReactNativeFactoryProtocol
|
|
9
10
|
|
|
10
|
-
public init(handlers: [ExpoReactDelegateHandler]) {
|
|
11
|
+
public init(handlers: [ExpoReactDelegateHandler], reactNativeFactory: ExpoReactNativeFactoryProtocol) {
|
|
11
12
|
self.handlers = handlers
|
|
13
|
+
self.reactNativeFactory = reactNativeFactory
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
@objc
|
|
@@ -23,7 +25,7 @@ public class ExpoReactDelegate: NSObject {
|
|
|
23
25
|
?? {
|
|
24
26
|
guard let appDelegate = (UIApplication.shared.delegate as? (any ReactNativeFactoryProvider)) ??
|
|
25
27
|
((UIApplication.shared.delegate as? NSObject)?.value(forKey: "_expoAppDelegate") as? (any ReactNativeFactoryProvider)) else {
|
|
26
|
-
|
|
28
|
+
return reactNativeFactory.recreateRootView(withBundleURL: nil, moduleName: moduleName, initialProps: initialProperties, launchOptions: launchOptions)
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
return appDelegate.recreateRootView(
|
|
@@ -245,6 +245,17 @@ class FunctionSpec: ExpoSpec {
|
|
|
245
245
|
@Field var property: String = "expo"
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
+
struct TestURLRecord: Record {
|
|
249
|
+
static let defaultURLString = "https://expo.dev"
|
|
250
|
+
static let defaultURL = URL(string: defaultURLString)!
|
|
251
|
+
|
|
252
|
+
@Field var url: URL = defaultURL
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
afterEach {
|
|
256
|
+
try runtime.eval("globalThis.result = undefined")
|
|
257
|
+
}
|
|
258
|
+
|
|
248
259
|
beforeSuite {
|
|
249
260
|
appContext.moduleRegistry.register(holder: mockModuleHolder(appContext) {
|
|
250
261
|
Name("TestModule")
|
|
@@ -286,10 +297,18 @@ class FunctionSpec: ExpoSpec {
|
|
|
286
297
|
return "\(f.property)"
|
|
287
298
|
}
|
|
288
299
|
|
|
300
|
+
Function("withURL") {
|
|
301
|
+
return TestURLRecord.defaultURL
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
Function("withNestedURL") {
|
|
305
|
+
return TestURLRecord()
|
|
306
|
+
}
|
|
307
|
+
|
|
289
308
|
Function("withOptionalRecord") { (f: TestRecord?) in
|
|
290
309
|
return "\(f?.property ?? "no value")"
|
|
291
310
|
}
|
|
292
|
-
|
|
311
|
+
|
|
293
312
|
Function("withSharedObject") {
|
|
294
313
|
return SharedString("Test")
|
|
295
314
|
}
|
|
@@ -305,7 +324,15 @@ class FunctionSpec: ExpoSpec {
|
|
|
305
324
|
AsyncFunction("withSharedObjectPromise") { (p: Promise) in
|
|
306
325
|
p.resolve(SharedString("Test with Promise"))
|
|
307
326
|
}
|
|
308
|
-
|
|
327
|
+
|
|
328
|
+
AsyncFunction("withURLAsync") {
|
|
329
|
+
return TestURLRecord.defaultURL
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
AsyncFunction("withNestedURLAsync") {
|
|
333
|
+
return TestURLRecord()
|
|
334
|
+
}
|
|
335
|
+
|
|
309
336
|
Class("Shared", SharedString.self) {
|
|
310
337
|
Property("value") { shared in
|
|
311
338
|
return shared.ref
|
|
@@ -360,7 +387,43 @@ class FunctionSpec: ExpoSpec {
|
|
|
360
387
|
it("accepts optional record") {
|
|
361
388
|
expect(try runtime.eval("expo.modules.TestModule.withOptionalRecord({property: \"123\"})").asString()) == "123"
|
|
362
389
|
}
|
|
363
|
-
|
|
390
|
+
|
|
391
|
+
it("returns URL (sync)") {
|
|
392
|
+
let result = try runtime.eval("globalThis.result = expo.modules.TestModule.withURL()")
|
|
393
|
+
expect(result.kind) == .string
|
|
394
|
+
expect(result.getString()) == TestURLRecord.defaultURLString
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
it("returns URL (async)") {
|
|
398
|
+
try runtime.eval("expo.modules.TestModule.withURLAsync().then((result) => { globalThis.result = result; })")
|
|
399
|
+
expect(safeBoolEval("!!globalThis.result")).toEventually(beTrue(), timeout: .milliseconds(2000))
|
|
400
|
+
|
|
401
|
+
let urlValue = try runtime.eval("url = globalThis.result")
|
|
402
|
+
expect(urlValue.kind) == .string
|
|
403
|
+
expect(urlValue.getString()) == TestURLRecord.defaultURLString
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
it("returns a record wit url (sync)") {
|
|
407
|
+
let object = try runtime.eval("globalThis.result = expo.modules.TestModule.withNestedURL()")
|
|
408
|
+
expect(object.kind) == .object
|
|
409
|
+
expect(object.getObject().hasProperty("url")) == true
|
|
410
|
+
expect(object.getObject().getProperty("url").getString()) == TestURLRecord.defaultURLString
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
it("returns a record with url (async)") {
|
|
414
|
+
try runtime.eval("expo.modules.TestModule.withNestedURLAsync().then((result) => { globalThis.result = result; })")
|
|
415
|
+
|
|
416
|
+
expect(safeBoolEval("!!globalThis.result.url")).toEventually(beTrue(), timeout: .milliseconds(2000))
|
|
417
|
+
|
|
418
|
+
let object = try runtime.eval("object = globalThis.result")
|
|
419
|
+
expect(object.kind) == .object
|
|
420
|
+
expect(object.getObject().hasProperty("url")) == true
|
|
421
|
+
|
|
422
|
+
let urlValue = try runtime.eval("object.url")
|
|
423
|
+
expect(urlValue.kind) == .string
|
|
424
|
+
expect(urlValue.getString()) == TestURLRecord.defaultURLString
|
|
425
|
+
}
|
|
426
|
+
|
|
364
427
|
it("returns a SharedObject (sync)") {
|
|
365
428
|
let object = try runtime.eval("expo.modules.TestModule.withSharedObject()")
|
|
366
429
|
|
|
@@ -385,7 +448,7 @@ class FunctionSpec: ExpoSpec {
|
|
|
385
448
|
expect(result.kind) == .string
|
|
386
449
|
expect(result.getString()) == "Test"
|
|
387
450
|
}
|
|
388
|
-
|
|
451
|
+
|
|
389
452
|
it("returns an Array of SharedObjects (async)") {
|
|
390
453
|
try runtime
|
|
391
454
|
.eval(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-modules-core",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.20",
|
|
4
4
|
"description": "The core of Expo Modules architecture",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -65,5 +65,5 @@
|
|
|
65
65
|
"@testing-library/react-native": "^13.2.0",
|
|
66
66
|
"expo-module-scripts": "^5.0.7"
|
|
67
67
|
},
|
|
68
|
-
"gitHead": "
|
|
68
|
+
"gitHead": "73d9bc0ee4f1e28cfda349dc36ee53d16fad0c5d"
|
|
69
69
|
}
|
|
@@ -26,18 +26,20 @@ export function useReleasingSharedObject<TSharedObject extends SharedObject>(
|
|
|
26
26
|
dependencies.every((value, index) => value === previousDependencies.current[index]);
|
|
27
27
|
|
|
28
28
|
// If the dependencies have changed, release the previous object and create a new one, otherwise this has been called
|
|
29
|
-
// because of
|
|
29
|
+
// because of an unrelated fast refresh, and we don't want to release the object.
|
|
30
30
|
if (!newObject || !dependenciesAreEqual) {
|
|
31
31
|
objectRef.current?.release();
|
|
32
32
|
newObject = factory();
|
|
33
33
|
objectRef.current = newObject;
|
|
34
34
|
previousDependencies.current = dependencies;
|
|
35
|
-
} else {
|
|
36
|
-
isFastRefresh.current = true;
|
|
37
35
|
}
|
|
38
36
|
return newObject;
|
|
39
37
|
}, dependencies);
|
|
40
38
|
|
|
39
|
+
useMemo(() => {
|
|
40
|
+
isFastRefresh.current = true;
|
|
41
|
+
}, []);
|
|
42
|
+
|
|
41
43
|
useEffect(() => {
|
|
42
44
|
isFastRefresh.current = false;
|
|
43
45
|
|