expo-modules-core 56.0.17 → 56.0.19
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 +18 -0
- package/android/build.gradle +2 -2
- package/android/src/compose/expo/modules/kotlin/views/ExpoComposeView.kt +10 -1
- package/android/src/main/cpp/JavaScriptObject.cpp +6 -0
- package/android/src/main/cpp/JavaScriptObject.h +5 -0
- package/android/src/main/cpp/installers/WorkletRuntimeInstaller.cpp +25 -1
- package/android/src/main/cpp/installers/WorkletRuntimeInstaller.h +9 -0
- package/android/src/main/java/expo/modules/kotlin/defaultmodules/CoreModule.kt +9 -18
- package/android/src/main/java/expo/modules/kotlin/jni/WorkletRuntimeInstaller.kt +11 -0
- package/build/ts-declarations/global.d.ts +4 -3
- package/build/ts-declarations/global.d.ts.map +1 -1
- package/build/worklets/index.d.ts +8 -1
- package/build/worklets/index.d.ts.map +1 -1
- package/ios/Core/AppContext.swift +36 -8
- package/ios/Core/Modules/CoreModule.swift +3 -10
- package/ios/Worklets/Core/WorkletIntegration.swift +7 -14
- package/ios/Worklets/EXWorkletsProvider.h +4 -0
- package/ios/Worklets/WorkletRuntimeResolver.h +23 -0
- package/ios/Worklets/WorkletRuntimeResolver.mm +15 -0
- package/ios/WorkletsAdapter/ExpoWorkletsBridgeProvider.mm +21 -0
- package/ios/WorkletsTests/WorkletsNotInstalledTests.swift +20 -0
- package/package.json +3 -3
- package/prebuilds/output/debug/xcframeworks/ExpoModulesCore.tar.gz +0 -0
- package/prebuilds/output/debug/xcframeworks/ExpoModulesWorklets.tar.gz +0 -0
- package/prebuilds/output/release/xcframeworks/ExpoModulesCore.tar.gz +0 -0
- package/prebuilds/output/release/xcframeworks/ExpoModulesWorklets.tar.gz +0 -0
- package/src/polyfill/dangerous-internal.ts +1 -1
- package/src/ts-declarations/global.ts +4 -3
- package/src/worklets/index.ts +10 -3
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,24 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 56.0.19 — 2026-07-03
|
|
14
|
+
|
|
15
|
+
### 🐛 Bug fixes
|
|
16
|
+
|
|
17
|
+
- [iOS] Fix `ExpoModulesProvider` lookup failing for bundle names with non-identifier characters (e.g. dots). ([#46424](https://github.com/expo/expo/pull/46424) by [@shawnthye-guru](https://github.com/shawnthye-guru))
|
|
18
|
+
|
|
19
|
+
## 56.0.18 — 2026-07-01
|
|
20
|
+
|
|
21
|
+
### 🐛 Bug fixes
|
|
22
|
+
|
|
23
|
+
- [Android][compose] Guard `onLayout` against detached window to prevent `LayoutNode should be attached to an owner` crash. ([#47085](https://github.com/expo/expo/pull/47085) by [@roitium](https://github.com/roitium))
|
|
24
|
+
- [Android] Fix Jetpack Compose `Host` content disappearing before a react-native-screens pop animation finishes, by deferring composition disposal to window detach while the view is still on-screen for a transition. ([#45914](https://github.com/expo/expo/issues/45914), [#47086](https://github.com/expo/expo/issues/47086) by [@aubrey-wodonga](https://github.com/aubrey-wodonga)) ([#47099](https://github.com/expo/expo/pull/47099) by [@nishan](https://github.com/intergalacticspacehighway))
|
|
25
|
+
- [iOS] Fix a reload deadlock on the new architecture where reloading (from pressing `r` in the iOS simulator) would freeze the app until the watchdog killed it. `reloadAppAsync` now triggers the reload synchronously when already on the main thread instead of always deferring via `DispatchQueue.main.async`. ([#47392](https://github.com/expo/expo/pull/47392) by [@brentvatne](https://github.com/brentvatne))
|
|
26
|
+
|
|
27
|
+
### 💡 Others
|
|
28
|
+
|
|
29
|
+
- [iOS][android] Resolve the worklet UI runtime from its `react-native-worklets` holder instead of the reanimated `_WORKLET_RUNTIME` global; `installOnUIRuntime` now takes the holder from `getUIRuntimeHolder()`. ([#46922](https://github.com/expo/expo/pull/46922), [#46935](https://github.com/expo/expo/pull/46935) by [@nishan](https://github.com/intergalacticspacehighway))
|
|
30
|
+
|
|
13
31
|
## 56.0.17 — 2026-06-15
|
|
14
32
|
|
|
15
33
|
### 🐛 Bug fixes
|
package/android/build.gradle
CHANGED
|
@@ -27,7 +27,7 @@ if (shouldIncludeCompose) {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
group = 'host.exp.exponent'
|
|
30
|
-
version = '56.0.
|
|
30
|
+
version = '56.0.19'
|
|
31
31
|
|
|
32
32
|
def isExpoModulesCoreTests = {
|
|
33
33
|
Gradle gradle = getGradle()
|
|
@@ -94,7 +94,7 @@ android {
|
|
|
94
94
|
defaultConfig {
|
|
95
95
|
consumerProguardFiles 'proguard-rules.pro'
|
|
96
96
|
versionCode 1
|
|
97
|
-
versionName "56.0.
|
|
97
|
+
versionName "56.0.19"
|
|
98
98
|
buildConfigField "String", "EXPO_MODULES_CORE_VERSION", "\"${versionName}\""
|
|
99
99
|
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", "true"
|
|
100
100
|
|
|
@@ -88,6 +88,9 @@ abstract class ExpoComposeView<T : ComposeProps>(
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
91
|
+
if (shouldUseAndroidLayout && !isAttachedToWindow) {
|
|
92
|
+
return
|
|
93
|
+
}
|
|
91
94
|
super.onLayout(changed, left, top, right, bottom)
|
|
92
95
|
|
|
93
96
|
// Makes sure the child ComposeView is sticky with the current hosting view
|
|
@@ -245,7 +248,13 @@ abstract class ExpoComposeView<T : ComposeProps>(
|
|
|
245
248
|
// registered on the Activity, which leaks this view.
|
|
246
249
|
// Swapping the strategy first detaches that observer, then we dispose.
|
|
247
250
|
it.setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnDetachedFromWindow)
|
|
248
|
-
it
|
|
251
|
+
// If the view is still attached when RN drops it, react-native-screens is keeping it
|
|
252
|
+
// on-screen for an in-progress navigation transition (e.g. a pop). Disposing now blanks
|
|
253
|
+
// the Compose content before the animation finishes (https://github.com/expo/expo/issues/47086).
|
|
254
|
+
// View eventually gets decomposed when RN screen detatches view from window
|
|
255
|
+
if (!it.isAttachedToWindow) {
|
|
256
|
+
it.disposeComposition()
|
|
257
|
+
}
|
|
249
258
|
}
|
|
250
259
|
}
|
|
251
260
|
|
|
@@ -58,6 +58,12 @@ std::shared_ptr<jsi::Object> JavaScriptObject::get() {
|
|
|
58
58
|
return jsObject;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
jsi::Runtime &JavaScriptObject::getRuntime() {
|
|
62
|
+
auto runtime = runtimeHolder.lock();
|
|
63
|
+
assert((runtime != nullptr) && "JS Runtime was used after deallocation");
|
|
64
|
+
return runtime->get();
|
|
65
|
+
}
|
|
66
|
+
|
|
61
67
|
bool JavaScriptObject::hasProperty(const std::string &name) {
|
|
62
68
|
auto runtime = runtimeHolder.lock();
|
|
63
69
|
assert((runtime != nullptr) && "JS Runtime was used after deallocation");
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#if WORKLETS_ENABLED
|
|
4
4
|
|
|
5
5
|
#include "../worklets/WorkletJSCallInvoker.h"
|
|
6
|
+
#include <worklets/Compat/StableApi.h>
|
|
6
7
|
#include <worklets/WorkletRuntime/WorkletRuntime.h>
|
|
7
8
|
|
|
8
9
|
#endif
|
|
@@ -15,7 +16,8 @@ namespace expo {
|
|
|
15
16
|
|
|
16
17
|
void WorkletRuntimeInstaller::registerNatives() {
|
|
17
18
|
javaClassLocal()->registerNatives({
|
|
18
|
-
makeNativeMethod("install", WorkletRuntimeInstaller::install)
|
|
19
|
+
makeNativeMethod("install", WorkletRuntimeInstaller::install),
|
|
20
|
+
makeNativeMethod("resolveUIRuntimePointer", WorkletRuntimeInstaller::resolveUIRuntimePointer)
|
|
19
21
|
});
|
|
20
22
|
}
|
|
21
23
|
|
|
@@ -44,6 +46,28 @@ jni::local_ref<JSIContext::javaobject> WorkletRuntimeInstaller::install(
|
|
|
44
46
|
#endif
|
|
45
47
|
}
|
|
46
48
|
|
|
49
|
+
jlong WorkletRuntimeInstaller::resolveUIRuntimePointer(
|
|
50
|
+
jni::alias_ref<jni::JClass>,
|
|
51
|
+
jni::alias_ref<JavaScriptObject::javaobject> uiRuntimeHolder
|
|
52
|
+
) noexcept {
|
|
53
|
+
#if WORKLETS_ENABLED
|
|
54
|
+
auto *holder = uiRuntimeHolder->cthis();
|
|
55
|
+
jsi::Runtime &runtime = holder->getRuntime();
|
|
56
|
+
std::shared_ptr<jsi::Object> holderObject = holder->get();
|
|
57
|
+
|
|
58
|
+
std::shared_ptr<worklets::WorkletRuntime> workletRuntime =
|
|
59
|
+
worklets::getWorkletRuntimeFromHolder(runtime, *holderObject);
|
|
60
|
+
if (!workletRuntime) {
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
jsi::Runtime &uiRuntime = worklets::getJSIRuntimeFromWorkletRuntime(workletRuntime);
|
|
65
|
+
return reinterpret_cast<jlong>(&uiRuntime);
|
|
66
|
+
#else
|
|
67
|
+
return 0;
|
|
68
|
+
#endif
|
|
69
|
+
}
|
|
70
|
+
|
|
47
71
|
void WorkletRuntimeInstaller::prepareRuntime(
|
|
48
72
|
jni::local_ref<JSIContext::javaobject> jsiContext
|
|
49
73
|
) noexcept {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
3
|
#include "../ExpoHeader.pch"
|
|
4
|
+
#include "../JavaScriptObject.h"
|
|
4
5
|
#include "MainRuntimeInstaller.h"
|
|
5
6
|
|
|
6
7
|
namespace jni = facebook::jni;
|
|
@@ -24,6 +25,14 @@ public:
|
|
|
24
25
|
static void prepareRuntime(
|
|
25
26
|
jni::local_ref<JSIContext::javaobject> jsiContext
|
|
26
27
|
) noexcept;
|
|
28
|
+
|
|
29
|
+
// Resolves the raw UI `jsi::Runtime *` (as a jlong) from a
|
|
30
|
+
// `react-native-worklets` UI runtime holder. Returns 0 when worklets isn't
|
|
31
|
+
// linked or the holder doesn't wrap a worklet runtime.
|
|
32
|
+
static jlong resolveUIRuntimePointer(
|
|
33
|
+
jni::alias_ref<jni::JClass> clazz,
|
|
34
|
+
jni::alias_ref<JavaScriptObject::javaobject> uiRuntimeHolder
|
|
35
|
+
) noexcept;
|
|
27
36
|
};
|
|
28
37
|
|
|
29
38
|
} // namespace expo
|
|
@@ -6,6 +6,8 @@ import com.facebook.react.ReactActivity
|
|
|
6
6
|
import expo.modules.BuildConfig
|
|
7
7
|
import expo.modules.kotlin.events.normalizeEventName
|
|
8
8
|
import expo.modules.kotlin.exception.Exceptions
|
|
9
|
+
import expo.modules.kotlin.jni.JavaScriptObject
|
|
10
|
+
import expo.modules.kotlin.jni.WorkletRuntimeInstaller
|
|
9
11
|
import expo.modules.kotlin.modules.DEFAULT_MODULE_VIEW
|
|
10
12
|
import expo.modules.kotlin.modules.Module
|
|
11
13
|
import expo.modules.kotlin.modules.ModuleDefinition
|
|
@@ -95,26 +97,15 @@ class CoreModule : Module() {
|
|
|
95
97
|
reactDelegate.reload()
|
|
96
98
|
}
|
|
97
99
|
|
|
98
|
-
Function("installOnUIRuntime") {
|
|
99
|
-
val
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
val uiRuntimePointerHolderObj = uiRuntimePointerHolder.getObject()
|
|
107
|
-
assert(uiRuntimePointerHolderObj.isArrayBuffer()) {
|
|
108
|
-
"Expected _WORKLET_RUNTIME to be an ArrayBuffer, got: ${uiRuntimePointerHolder.kind()}."
|
|
100
|
+
Function("installOnUIRuntime") { uiRuntimeHolder: JavaScriptObject ->
|
|
101
|
+
val runtimePointer = WorkletRuntimeInstaller.resolveUIRuntimePointer(uiRuntimeHolder)
|
|
102
|
+
if (runtimePointer == 0L) {
|
|
103
|
+
throw IllegalStateException(
|
|
104
|
+
"Couldn't resolve the worklets UI runtime from the provided holder. " +
|
|
105
|
+
"Make sure `react-native-worklets` is installed and rebuild the app."
|
|
106
|
+
)
|
|
109
107
|
}
|
|
110
108
|
|
|
111
|
-
val arayBuffer = uiRuntimePointerHolderObj.getArrayBuffer()
|
|
112
|
-
assert(arayBuffer.size() == 8) {
|
|
113
|
-
"Expected _WORKLET_RUNTIME to be an ArrayBuffer with a size of 8 bytes (a pointer size), got: ${arayBuffer.size()}."
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
val runtimePointer = arayBuffer.read8Byte(0)
|
|
117
|
-
|
|
118
109
|
runBlocking {
|
|
119
110
|
withContext(Dispatchers.Main) {
|
|
120
111
|
appContext.uiRuntime.install(runtimePointer)
|
|
@@ -22,4 +22,15 @@ class WorkletRuntimeInstaller(
|
|
|
22
22
|
jsRuntimePointer: Long,
|
|
23
23
|
jniDeallocator: JNIDeallocator
|
|
24
24
|
): JSIContext
|
|
25
|
+
|
|
26
|
+
companion object {
|
|
27
|
+
/**
|
|
28
|
+
* Resolves the raw UI `jsi::Runtime*` (as a Long) from a `react-native-worklets`
|
|
29
|
+
* UI runtime holder. Returns 0 when worklets isn't installed or the holder
|
|
30
|
+
* doesn't wrap a worklet runtime.
|
|
31
|
+
*/
|
|
32
|
+
@JvmStatic
|
|
33
|
+
@Suppress("KotlinJniMissingFunction")
|
|
34
|
+
external fun resolveUIRuntimePointer(uiRuntimeHolder: JavaScriptObject): Long
|
|
35
|
+
}
|
|
25
36
|
}
|
|
@@ -79,10 +79,11 @@ declare namespace ExpoGlobal {
|
|
|
79
79
|
*/
|
|
80
80
|
export function reloadAppAsync(reason: string): Promise<void>;
|
|
81
81
|
/**
|
|
82
|
-
* Installs Expo Modules inside the
|
|
83
|
-
* @
|
|
82
|
+
* Installs Expo Modules inside the worklets UI runtime.
|
|
83
|
+
* @param uiRuntimeHolder The UI runtime holder from `getUIRuntimeHolder()` in
|
|
84
|
+
* `react-native-worklets`.
|
|
84
85
|
*/
|
|
85
|
-
export function installOnUIRuntime(): void;
|
|
86
|
+
export function installOnUIRuntime(uiRuntimeHolder: object): void;
|
|
86
87
|
}
|
|
87
88
|
declare global {
|
|
88
89
|
namespace NodeJS {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"global.d.ts","sourceRoot":"","sources":["../../src/ts-declarations/global.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,KAAK,UAAU,GAAG;IAChB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACrC,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChE,CAAC;AAEF,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,aAAa,GAAG,YAAY,GAAG,MAAM,CAAC;IAChD,qCAAqC;IACrC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAEhC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B,gKAAgK;IAChK,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,sKAAsK;IACtK,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,0LAA0L;IAC1L,gBAAgB,CAAC,EAAE,KAAK,GAAG,SAAS,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAErD,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,cAAc,CAAC;IACpB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED;;;GAGG;AACH,OAAO,WAAW,UAAU,CAAC;IAC3B,8DAA8D;IAC9D,MAAM,CAAC,IAAI,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAGxC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,OAAO,EAAE,YAAY,EAAE,CAAC;IAIxB;;;;OAIG;IACH,MAAM,CAAC,MAAM,sBAAsB,EAC/B,SAAS,GACT;QACE,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IAEN;;;;OAIG;IACH,MAAM,CAAC,MAAM,QAAQ,EAAE,SAAS,GAAG,MAAM,CAAC;IAE1C;;;;OAIG;IACH,MAAM,CAAC,MAAM,YAAY,EAAE,SAAS,GAAG,MAAM,CAAC;IAI9C;;OAEG;IACH,MAAM,UAAU,MAAM,IAAI,MAAM,CAAC;IAEjC;;OAEG;IACH,MAAM,UAAU,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAEhE;;;OAGG;IACH,MAAM,UAAU,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC;IAExF;;OAEG;IACH,MAAM,UAAU,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9D
|
|
1
|
+
{"version":3,"file":"global.d.ts","sourceRoot":"","sources":["../../src/ts-declarations/global.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Installs Expo Modules on the UI worklet runtime so worklet callbacks and
|
|
3
|
+
* serializable SharedObjects work there.
|
|
4
|
+
*
|
|
5
|
+
* @param uiRuntimeHolder The UI runtime holder from `getUIRuntimeHolder()` in
|
|
6
|
+
* `react-native-worklets`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function installOnUIRuntime(uiRuntimeHolder: object): void;
|
|
2
9
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/worklets/index.ts"],"names":[],"mappings":"AAAA,wBAAgB,kBAAkB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/worklets/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,eAAe,EAAE,MAAM,QASzD"}
|
|
@@ -644,9 +644,18 @@ public final class AppContext: NSObject, EXAppContextProtocol, @unchecked Sendab
|
|
|
644
644
|
public static func modulesProvider(withName providerName: String = "ExpoModulesProvider") -> ModulesProvider {
|
|
645
645
|
// [0] When ExpoModulesCore is built as separated framework/module,
|
|
646
646
|
// we should explicitly load main bundle's `ExpoModulesProvider` class.
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
647
|
+
// CFBundleExecutable is tried first: it equals $(PRODUCT_NAME:c99extidentifier) and
|
|
648
|
+
// directly matches the Swift module name. CFBundleName is kept as a fallback for the
|
|
649
|
+
// uncommon case where both values are identical valid identifiers.
|
|
650
|
+
let mainBundleNames = [
|
|
651
|
+
Bundle.main.infoDictionary?["CFBundleExecutable"],
|
|
652
|
+
Bundle.main.infoDictionary?["CFBundleName"]
|
|
653
|
+
].compactMap { $0 as? String }
|
|
654
|
+
|
|
655
|
+
for providerClassName in moduleProviderClassNames(withName: providerName, bundleNames: mainBundleNames) {
|
|
656
|
+
if let providerClass = NSClassFromString(providerClassName) as? ModulesProvider.Type {
|
|
657
|
+
return providerClass.init()
|
|
658
|
+
}
|
|
650
659
|
}
|
|
651
660
|
|
|
652
661
|
// [1] Fallback to `ExpoModulesProvider` class from the current module.
|
|
@@ -656,9 +665,15 @@ public final class AppContext: NSObject, EXAppContextProtocol, @unchecked Sendab
|
|
|
656
665
|
|
|
657
666
|
// [2] Fallback to search for `ExpoModulesProvider` in frameworks (brownfield use case)
|
|
658
667
|
for bundle in Bundle.allFrameworks {
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
668
|
+
let frameworkBundleNames = [
|
|
669
|
+
bundle.infoDictionary?["CFBundleExecutable"],
|
|
670
|
+
bundle.infoDictionary?["CFBundleName"]
|
|
671
|
+
].compactMap { $0 as? String }
|
|
672
|
+
|
|
673
|
+
for providerClassName in moduleProviderClassNames(withName: providerName, bundleNames: frameworkBundleNames) {
|
|
674
|
+
if let providerClass = NSClassFromString(providerClassName) as? ModulesProvider.Type {
|
|
675
|
+
return providerClass.init()
|
|
676
|
+
}
|
|
662
677
|
}
|
|
663
678
|
}
|
|
664
679
|
|
|
@@ -666,12 +681,25 @@ public final class AppContext: NSObject, EXAppContextProtocol, @unchecked Sendab
|
|
|
666
681
|
return ModulesProvider()
|
|
667
682
|
}
|
|
668
683
|
|
|
684
|
+
internal static func moduleProviderClassNames(withName providerName: String, bundleNames: [String]) -> [String] {
|
|
685
|
+
var seen = Set<String>()
|
|
686
|
+
return bundleNames.compactMap { bundleName in
|
|
687
|
+
let candidate = "\(bundleName).\(providerName)"
|
|
688
|
+
return seen.insert(candidate).inserted ? candidate : nil
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
669
692
|
public func reloadAppAsync(_ reason: String = "Reload from appContext") {
|
|
670
693
|
if moduleRegistry.has(moduleWithName: "ExpoGo") {
|
|
671
694
|
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "EXReloadActiveAppRequest"), object: nil)
|
|
672
695
|
} else {
|
|
673
|
-
|
|
674
|
-
|
|
696
|
+
// Must run on the main thread (see #31789), but synchronously when already there — deferring
|
|
697
|
+
// via `DispatchQueue.main.async` deadlocks the bridgeless reload against TurboModule teardown.
|
|
698
|
+
let trigger = { RCTTriggerReloadCommandListeners(reason) }
|
|
699
|
+
if Thread.isMainThread {
|
|
700
|
+
trigger()
|
|
701
|
+
} else {
|
|
702
|
+
DispatchQueue.main.async(execute: trigger)
|
|
675
703
|
}
|
|
676
704
|
}
|
|
677
705
|
}
|
|
@@ -4,8 +4,6 @@ internal import React
|
|
|
4
4
|
import Foundation
|
|
5
5
|
import ExpoModulesJSI
|
|
6
6
|
|
|
7
|
-
private let WORKLET_RUNTIME_KEY = "_WORKLET_RUNTIME"
|
|
8
|
-
|
|
9
7
|
// The core module that describes the `global.expo` object.
|
|
10
8
|
internal final class CoreModule: Module {
|
|
11
9
|
internal func definition() -> ModuleDefinition {
|
|
@@ -29,7 +27,7 @@ internal final class CoreModule: Module {
|
|
|
29
27
|
FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.path ?? ""
|
|
30
28
|
}
|
|
31
29
|
|
|
32
|
-
Function("installOnUIRuntime") {
|
|
30
|
+
Function("installOnUIRuntime") { (uiRuntimeHolder: JavaScriptValue) in
|
|
33
31
|
guard let appContext else {
|
|
34
32
|
throw Exceptions.AppContextLost()
|
|
35
33
|
}
|
|
@@ -40,12 +38,7 @@ internal final class CoreModule: Module {
|
|
|
40
38
|
}
|
|
41
39
|
|
|
42
40
|
let runtime = try appContext.runtime
|
|
43
|
-
|
|
44
|
-
throw WorkletUIRuntimeException()
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
let pointerHolder = runtime.global().getProperty(WORKLET_RUNTIME_KEY)
|
|
48
|
-
if !pointerHolder.isObject() {
|
|
41
|
+
guard uiRuntimeHolder.isObject() else {
|
|
49
42
|
throw WorkletUIRuntimeException()
|
|
50
43
|
}
|
|
51
44
|
|
|
@@ -61,7 +54,7 @@ internal final class CoreModule: Module {
|
|
|
61
54
|
|
|
62
55
|
let block = {
|
|
63
56
|
do {
|
|
64
|
-
let uiRuntime = try factory(appContext,
|
|
57
|
+
let uiRuntime = try factory(appContext, uiRuntimeHolder, runtime)
|
|
65
58
|
appContext._uiRuntime = uiRuntime
|
|
66
59
|
} catch {
|
|
67
60
|
errorHolder.error = error
|
|
@@ -8,20 +8,13 @@ import ExpoModulesJSI
|
|
|
8
8
|
@objc(EXWorkletIntegration)
|
|
9
9
|
public final class WorkletIntegration: NSObject {
|
|
10
10
|
@objc public static func register() {
|
|
11
|
-
AppContext.uiRuntimeFactory = { _,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
guard pointerValue.isArrayBuffer() else {
|
|
17
|
-
throw WorkletRuntimePointerExtractionException()
|
|
18
|
-
}
|
|
19
|
-
let arrayBuffer = pointerValue.getArrayBuffer()
|
|
20
|
-
guard arrayBuffer.size == MemoryLayout<UnsafeMutableRawPointer>.size else {
|
|
21
|
-
throw WorkletRuntimePointerExtractionException()
|
|
11
|
+
AppContext.uiRuntimeFactory = { _, holder, runtime in
|
|
12
|
+
let runtimePointer: UnsafeMutableRawPointer? = runtime.withUnsafePointee { runtimePointee in
|
|
13
|
+
holder.withUnsafePointee { holderPointee in
|
|
14
|
+
WorkletRuntimeResolver.uiRuntimePointer(runtimePointer: runtimePointee, holderPointer: holderPointee)
|
|
15
|
+
}
|
|
22
16
|
}
|
|
23
|
-
let
|
|
24
|
-
guard let workletRuntime = WorkletRuntime(runtimePointer: pointer) else {
|
|
17
|
+
guard let runtimePointer, let workletRuntime = WorkletRuntime(runtimePointer: runtimePointer) else {
|
|
25
18
|
throw WorkletRuntimePointerExtractionException()
|
|
26
19
|
}
|
|
27
20
|
return workletRuntime
|
|
@@ -29,7 +22,7 @@ public final class WorkletIntegration: NSObject {
|
|
|
29
22
|
}
|
|
30
23
|
}
|
|
31
24
|
|
|
32
|
-
|
|
25
|
+
internal final class WorkletRuntimePointerExtractionException: Exception, @unchecked Sendable {
|
|
33
26
|
override var reason: String {
|
|
34
27
|
"Cannot extract pointer to UI worklet runtime"
|
|
35
28
|
}
|
|
@@ -25,6 +25,10 @@ NS_SWIFT_NAME(WorkletsProvider)
|
|
|
25
25
|
- (nullable id)workletRuntimeHandleForRawPointer:(void *)rawPointer
|
|
26
26
|
NS_SWIFT_NAME(workletRuntimeHandle(rawPointer:));
|
|
27
27
|
|
|
28
|
+
- (void * _Nullable)uiRuntimePointerWithRuntimePointer:(void *)runtimePointer
|
|
29
|
+
holderPointer:(const void *)holderPointer
|
|
30
|
+
NS_SWIFT_NAME(uiRuntimePointer(runtimePointer:holderPointer:));
|
|
31
|
+
|
|
28
32
|
- (void)scheduleWorkletWithRuntimeHandle:(id)runtimeHandle
|
|
29
33
|
serializable:(EXJavaScriptSerializable *)serializable
|
|
30
34
|
arguments:(NSArray *)arguments
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Copyright 2025-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
#import <Foundation/Foundation.h>
|
|
4
|
+
|
|
5
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
6
|
+
|
|
7
|
+
NS_SWIFT_NAME(WorkletRuntimeResolver)
|
|
8
|
+
@interface EXWorkletRuntimeResolver : NSObject
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
Resolves the raw `jsi::Runtime *` of the UI worklet runtime from a
|
|
12
|
+
`react-native-worklets` UI runtime holder.
|
|
13
|
+
@param runtimePointer Raw pointer to the RN `jsi::Runtime`.
|
|
14
|
+
@param holderPointer Raw pointer to the holder `jsi::Value`.
|
|
15
|
+
Returns NULL when the runtime can't be resolved.
|
|
16
|
+
*/
|
|
17
|
+
+ (void * _Nullable)uiRuntimePointerWithRuntimePointer:(void *)runtimePointer
|
|
18
|
+
holderPointer:(const void *)holderPointer
|
|
19
|
+
NS_SWIFT_NAME(uiRuntimePointer(runtimePointer:holderPointer:));
|
|
20
|
+
|
|
21
|
+
@end
|
|
22
|
+
|
|
23
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Copyright 2025-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
#import <ExpoModulesWorklets/WorkletRuntimeResolver.h>
|
|
4
|
+
#import <ExpoModulesWorklets/EXWorkletsProvider.h>
|
|
5
|
+
|
|
6
|
+
@implementation EXWorkletRuntimeResolver
|
|
7
|
+
|
|
8
|
+
+ (void * _Nullable)uiRuntimePointerWithRuntimePointer:(void *)runtimePointer
|
|
9
|
+
holderPointer:(const void *)holderPointer
|
|
10
|
+
{
|
|
11
|
+
id<EXWorkletsProvider> provider = EXWorkletsProviderRegistry.shared;
|
|
12
|
+
return [provider uiRuntimePointerWithRuntimePointer:runtimePointer holderPointer:holderPointer];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@end
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
#include <memory>
|
|
9
9
|
#include <vector>
|
|
10
|
+
#include <worklets/Compat/StableApi.h>
|
|
10
11
|
#include <worklets/SharedItems/Serializable.h>
|
|
11
12
|
#include <worklets/WorkletRuntime/WorkletRuntime.h>
|
|
12
13
|
|
|
@@ -163,6 +164,26 @@ static jsi::Value callWorklet(jsi::Runtime &rt, std::shared_ptr<worklets::Serial
|
|
|
163
164
|
return handle;
|
|
164
165
|
}
|
|
165
166
|
|
|
167
|
+
- (void * _Nullable)uiRuntimePointerWithRuntimePointer:(void *)runtimePointer
|
|
168
|
+
holderPointer:(const void *)holderPointer
|
|
169
|
+
{
|
|
170
|
+
jsi::Runtime &rt = *reinterpret_cast<jsi::Runtime *>(runtimePointer);
|
|
171
|
+
const jsi::Value &holderValue = *reinterpret_cast<const jsi::Value *>(holderPointer);
|
|
172
|
+
|
|
173
|
+
if (!holderValue.isObject()) {
|
|
174
|
+
return nullptr;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
jsi::Object holder = holderValue.getObject(rt);
|
|
178
|
+
std::shared_ptr<worklets::WorkletRuntime> workletRuntime = worklets::getWorkletRuntimeFromHolder(rt, holder);
|
|
179
|
+
if (!workletRuntime) {
|
|
180
|
+
return nullptr;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
jsi::Runtime &uiRuntime = worklets::getJSIRuntimeFromWorkletRuntime(workletRuntime);
|
|
184
|
+
return &uiRuntime;
|
|
185
|
+
}
|
|
186
|
+
|
|
166
187
|
- (void)scheduleWorkletWithRuntimeHandle:(id)runtimeHandle
|
|
167
188
|
serializable:(EXJavaScriptSerializable *)serializable
|
|
168
189
|
arguments:(NSArray *)arguments
|
|
@@ -30,4 +30,24 @@ struct WorkletsNotInstalledTests {
|
|
|
30
30
|
_ = try dynamicType.cast(jsValue: jsValue, appContext: appContext)
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
+
|
|
34
|
+
@Test
|
|
35
|
+
func `UI runtime install fails with an error (not a crash) when the worklets adapter is not linked`() throws {
|
|
36
|
+
// Only meaningful while the adapter is unlinked (the default in this test harness).
|
|
37
|
+
try #require(WorkletsProviderRegistry.shared == nil)
|
|
38
|
+
|
|
39
|
+
// Installs the core-side hook that turns a UI runtime holder into a runtime.
|
|
40
|
+
WorkletIntegration.register()
|
|
41
|
+
let factory = try #require(AppContext.uiRuntimeFactory)
|
|
42
|
+
|
|
43
|
+
let appContext = AppContext.create()
|
|
44
|
+
let runtime = try appContext.runtime
|
|
45
|
+
// With no adapter linked the resolver returns NULL for any holder, so the
|
|
46
|
+
// factory must throw rather than dereference a null runtime pointer.
|
|
47
|
+
let holder = try runtime.eval("({})")
|
|
48
|
+
|
|
49
|
+
#expect(throws: WorkletRuntimePointerExtractionException.self) {
|
|
50
|
+
_ = try factory(appContext, holder, runtime)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
33
53
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-modules-core",
|
|
3
|
-
"version": "56.0.
|
|
3
|
+
"version": "56.0.19",
|
|
4
4
|
"description": "The core of Expo Modules architecture",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@expo/expo-modules-macros-plugin": "0.2.2",
|
|
50
|
-
"expo-modules-jsi": "~56.0.
|
|
50
|
+
"expo-modules-jsi": "~56.0.11",
|
|
51
51
|
"invariant": "^2.2.4"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"@types/invariant": "^2.2.33",
|
|
67
67
|
"expo-module-scripts": "56.0.3"
|
|
68
68
|
},
|
|
69
|
-
"gitHead": "
|
|
69
|
+
"gitHead": "b2e161a54f90a778ab7e5560c0c8f021bbfcaae2",
|
|
70
70
|
"scripts": {
|
|
71
71
|
"build": "expo-module build",
|
|
72
72
|
"clean": "expo-module clean",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -102,10 +102,11 @@ declare namespace ExpoGlobal {
|
|
|
102
102
|
export function reloadAppAsync(reason: string): Promise<void>;
|
|
103
103
|
|
|
104
104
|
/**
|
|
105
|
-
* Installs Expo Modules inside the
|
|
106
|
-
* @
|
|
105
|
+
* Installs Expo Modules inside the worklets UI runtime.
|
|
106
|
+
* @param uiRuntimeHolder The UI runtime holder from `getUIRuntimeHolder()` in
|
|
107
|
+
* `react-native-worklets`.
|
|
107
108
|
*/
|
|
108
|
-
export function installOnUIRuntime(): void;
|
|
109
|
+
export function installOnUIRuntime(uiRuntimeHolder: object): void;
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
/* eslint-disable no-var */
|
package/src/worklets/index.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Installs Expo Modules on the UI worklet runtime so worklet callbacks and
|
|
3
|
+
* serializable SharedObjects work there.
|
|
4
|
+
*
|
|
5
|
+
* @param uiRuntimeHolder The UI runtime holder from `getUIRuntimeHolder()` in
|
|
6
|
+
* `react-native-worklets`.
|
|
7
|
+
*/
|
|
8
|
+
export function installOnUIRuntime(uiRuntimeHolder: object) {
|
|
2
9
|
const installOnUIRuntimeNative = globalThis?.expo?.installOnUIRuntime;
|
|
3
10
|
if (!installOnUIRuntimeNative) {
|
|
4
11
|
throw Error(
|
|
5
|
-
"Native method to install Expo Modules on
|
|
12
|
+
"Native method to install Expo Modules on the worklets UI runtime wasn't found in `expo-modules-core`."
|
|
6
13
|
);
|
|
7
14
|
}
|
|
8
15
|
|
|
9
|
-
installOnUIRuntimeNative();
|
|
16
|
+
installOnUIRuntimeNative(uiRuntimeHolder);
|
|
10
17
|
}
|