expo-modules-core 56.0.16 → 56.0.18
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 +19 -0
- package/android/build.gradle +2 -2
- package/android/src/compose/expo/modules/kotlin/views/ExpoComposeView.kt +58 -7
- package/android/src/main/cpp/JavaScriptObject.cpp +6 -0
- package/android/src/main/cpp/JavaScriptObject.h +5 -0
- package/android/src/main/cpp/fabric/NativeStatePropsGetter.cpp +2 -2
- 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/android/src/main/java/expo/modules/kotlin/jni/fabric/NativeStatePropsGetter.kt +26 -2
- package/android/src/main/java/expo/modules/kotlin/views/ViewManagerWrapperDelegate.kt +9 -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 +7 -2
- 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.18 — 2026-07-01
|
|
14
|
+
|
|
15
|
+
### 🐛 Bug fixes
|
|
16
|
+
|
|
17
|
+
- [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))
|
|
18
|
+
- [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))
|
|
19
|
+
- [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))
|
|
20
|
+
|
|
21
|
+
### 💡 Others
|
|
22
|
+
|
|
23
|
+
- [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))
|
|
24
|
+
|
|
25
|
+
## 56.0.17 — 2026-06-15
|
|
26
|
+
|
|
27
|
+
### 🐛 Bug fixes
|
|
28
|
+
|
|
29
|
+
- [Android] Fixed Expo UI re-compose when switching screens in react-native-screens. ([#46650](https://github.com/expo/expo/pull/46650) by [@kudo](https://github.com/kudo))
|
|
30
|
+
|
|
13
31
|
## 56.0.16 — 2026-06-10
|
|
14
32
|
|
|
15
33
|
### 🎉 New features
|
|
@@ -19,6 +37,7 @@
|
|
|
19
37
|
### 🐛 Bug fixes
|
|
20
38
|
|
|
21
39
|
- [android] Add a synchronous shadow node size update path, fixing a layout shift for `Host` `matchContents` views. ([#46604](https://github.com/expo/expo/pull/46604) by [@nishan](https://github.com/intergalacticspacehighway))
|
|
40
|
+
- [Android] Fix `NullPointerException` crash when a `matchContents` view is unmounted while a shadow node size update is pending (e.g. closing a bottom sheet mid-resize). ([#46785](https://github.com/expo/expo/pull/46785) by [@nishan](https://github.com/intergalacticspacehighway))
|
|
22
41
|
- [iOS] Accept JS `Double` timestamps in `Date` Convertible. JS numbers arrive across the JSI bridge as Swift `Double`; the prior `as? Int` branch never matched, throwing `ConvertingException<Date>` whenever a JS caller passed `someDate.getTime()` to a `Date` / `Date?` argument. ([#46340](https://github.com/expo/expo/pull/46340) by [@kyleasaff](https://github.com/kyleasaff))
|
|
23
42
|
- [Android] Fix events being silently dropped for Compose views in custom modules. ([#46623](https://github.com/expo/expo/issues/46623) by [@benjaminkomen](https://github.com/benjaminkomen)) ([#46624](https://github.com/expo/expo/pull/46624) by [@nishan](https://github.com/intergalacticspacehighway))
|
|
24
43
|
|
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.18'
|
|
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.18"
|
|
98
98
|
buildConfigField "String", "EXPO_MODULES_CORE_VERSION", "\"${versionName}\""
|
|
99
99
|
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", "true"
|
|
100
100
|
|
|
@@ -17,6 +17,10 @@ import androidx.annotation.UiThread
|
|
|
17
17
|
import androidx.compose.ui.platform.ComposeView
|
|
18
18
|
import androidx.compose.ui.platform.ViewCompositionStrategy
|
|
19
19
|
import androidx.core.view.size
|
|
20
|
+
import androidx.lifecycle.LifecycleOwner
|
|
21
|
+
import androidx.lifecycle.setViewTreeLifecycleOwner
|
|
22
|
+
import androidx.savedstate.SavedStateRegistryOwner
|
|
23
|
+
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
|
|
20
24
|
import expo.modules.kotlin.AppContext
|
|
21
25
|
import expo.modules.kotlin.exception.CodedException
|
|
22
26
|
import expo.modules.kotlin.types.enforceType
|
|
@@ -52,10 +56,14 @@ abstract class ExpoComposeView<T : ComposeProps>(
|
|
|
52
56
|
context: Context,
|
|
53
57
|
appContext: AppContext,
|
|
54
58
|
private val withHostingView: Boolean = false
|
|
55
|
-
) : ExpoView(context, appContext) {
|
|
59
|
+
) : ExpoView(context, appContext), ComposeHostingView {
|
|
56
60
|
open val props: T? = null
|
|
57
61
|
protected var recomposeScope: RecomposeScope? = null
|
|
58
62
|
|
|
63
|
+
// Retained so the composition can be disposed on unmount: its strategy is
|
|
64
|
+
// pinned to the Activity lifecycle, so nothing disposes it on window detach.
|
|
65
|
+
private var hostingComposeView: ComposeView? = null
|
|
66
|
+
|
|
59
67
|
private val globalEvent = ViewEvent<Pair<String, Map<String, Any?>>>(GLOBAL_EVENT_NAME, this, null)
|
|
60
68
|
|
|
61
69
|
/**
|
|
@@ -80,6 +88,9 @@ abstract class ExpoComposeView<T : ComposeProps>(
|
|
|
80
88
|
}
|
|
81
89
|
|
|
82
90
|
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
91
|
+
if (shouldUseAndroidLayout && !isAttachedToWindow) {
|
|
92
|
+
return
|
|
93
|
+
}
|
|
83
94
|
super.onLayout(changed, left, top, right, bottom)
|
|
84
95
|
|
|
85
96
|
// Makes sure the child ComposeView is sticky with the current hosting view
|
|
@@ -191,22 +202,62 @@ abstract class ExpoComposeView<T : ComposeProps>(
|
|
|
191
202
|
|
|
192
203
|
private fun addComposeView() {
|
|
193
204
|
val composeView = ComposeView(context).also {
|
|
205
|
+
// Give each Host a unique id so its rememberSaveable state gets its own key.
|
|
206
|
+
// All Hosts share the Activity's SavedStateRegistry (set below), so without an id
|
|
207
|
+
// they'd collide on one key and only the first could save/restore state.
|
|
208
|
+
it.id = generateViewId()
|
|
194
209
|
it.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
|
195
|
-
it
|
|
210
|
+
// Pin the composition to the Activity lifecycle so it survives
|
|
211
|
+
// react-native-screens detaching inactive screens on every switch.
|
|
212
|
+
// The strategy alone isn't enough: Compose's WrappedComposition also
|
|
213
|
+
// observes the view-tree lifecycle owner found at first attach — the
|
|
214
|
+
// screen fragment's, which RN-screens destroys per switch — and
|
|
215
|
+
// self-disposes on its ON_DESTROY, leaving a dead composition that
|
|
216
|
+
// never recreates. Overriding the owners on the ComposeView (nearest
|
|
217
|
+
// tag wins) points both at the Activity. Unmount disposes explicitly
|
|
218
|
+
// via disposeHostedComposition().
|
|
219
|
+
val activity = appContext.currentActivity
|
|
220
|
+
if (activity is LifecycleOwner && activity is SavedStateRegistryOwner) {
|
|
221
|
+
it.setViewTreeLifecycleOwner(activity)
|
|
222
|
+
it.setViewTreeSavedStateRegistryOwner(activity)
|
|
223
|
+
it.setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnLifecycleDestroyed(activity.lifecycle))
|
|
224
|
+
} else {
|
|
225
|
+
// No Activity to pin to: keep the prior behavior, including the
|
|
226
|
+
// dispose-on-reattach workaround for blank compositions after
|
|
227
|
+
// navigation (https://github.com/expo/expo/pull/34689).
|
|
228
|
+
it.setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
|
229
|
+
it.addOnAttachStateChangeListener(
|
|
230
|
+
OnAttachAfterDetachmentListener(onAttachAfterDetachment = {
|
|
231
|
+
it.disposeComposition()
|
|
232
|
+
})
|
|
233
|
+
)
|
|
234
|
+
}
|
|
196
235
|
it.setContent {
|
|
197
236
|
with(ComposableScope()) {
|
|
198
237
|
Content()
|
|
199
238
|
}
|
|
200
239
|
}
|
|
201
|
-
it.addOnAttachStateChangeListener(
|
|
202
|
-
OnAttachAfterDetachmentListener(onAttachAfterDetachment = {
|
|
203
|
-
it.disposeComposition()
|
|
204
|
-
})
|
|
205
|
-
)
|
|
206
240
|
}
|
|
241
|
+
hostingComposeView = composeView
|
|
207
242
|
addView(composeView)
|
|
208
243
|
}
|
|
209
244
|
|
|
245
|
+
override fun disposeHostedComposition() {
|
|
246
|
+
hostingComposeView?.let {
|
|
247
|
+
// disposeComposition() alone leaves the composition strategy's lifecycle observer
|
|
248
|
+
// registered on the Activity, which leaks this view.
|
|
249
|
+
// Swapping the strategy first detaches that observer, then we dispose.
|
|
250
|
+
it.setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnDetachedFromWindow)
|
|
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
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
210
261
|
override fun addView(child: View, index: Int, params: ViewGroup.LayoutParams) {
|
|
211
262
|
val view = if (child !is ExpoComposeView<*> && child !is ComposeView && this !is RNHostViewInterface) {
|
|
212
263
|
ExpoComposeAndroidView(child, appContext)
|
|
@@ -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");
|
|
@@ -9,8 +9,8 @@ namespace expo {
|
|
|
9
9
|
void NativeStatePropsGetter::registerNatives() {
|
|
10
10
|
javaClassLocal()->registerNatives({
|
|
11
11
|
makeNativeMethod("getStateProps", NativeStatePropsGetter::getStateProps),
|
|
12
|
-
makeNativeMethod("
|
|
13
|
-
makeNativeMethod("
|
|
12
|
+
makeNativeMethod("updateStyleSizeImmediateImpl", NativeStatePropsGetter::updateStyleSizeImmediate),
|
|
13
|
+
makeNativeMethod("updateViewSizeImmediateImpl", NativeStatePropsGetter::updateViewSizeImmediate),
|
|
14
14
|
});
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -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
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
package expo.modules.kotlin.jni.fabric
|
|
2
2
|
|
|
3
|
+
import com.facebook.jni.HybridData
|
|
3
4
|
import com.facebook.yoga.annotations.DoNotStrip
|
|
4
5
|
|
|
5
6
|
@DoNotStrip
|
|
@@ -8,8 +9,31 @@ class NativeStatePropsGetter {
|
|
|
8
9
|
external fun getStateProps(stateWrapper: Any): Map<String, Any?>?
|
|
9
10
|
|
|
10
11
|
// Synchronously flush a style property size update in the current frame (pass NaN for "unset").
|
|
11
|
-
|
|
12
|
+
fun updateStyleSizeImmediate(stateWrapper: Any, styleWidth: Double, styleHeight: Double) {
|
|
13
|
+
if (!isStateValid(stateWrapper)) {
|
|
14
|
+
return
|
|
15
|
+
}
|
|
16
|
+
updateStyleSizeImmediateImpl(stateWrapper, styleWidth, styleHeight)
|
|
17
|
+
}
|
|
12
18
|
|
|
13
19
|
// Synchronously flush a size update in the current frame.
|
|
14
|
-
|
|
20
|
+
fun updateViewSizeImmediate(stateWrapper: Any, width: Double, height: Double) {
|
|
21
|
+
if (!isStateValid(stateWrapper)) {
|
|
22
|
+
return
|
|
23
|
+
}
|
|
24
|
+
updateViewSizeImmediateImpl(stateWrapper, width, height)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private external fun updateStyleSizeImmediateImpl(
|
|
28
|
+
stateWrapper: Any,
|
|
29
|
+
styleWidth: Double,
|
|
30
|
+
styleHeight: Double
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
private external fun updateViewSizeImmediateImpl(stateWrapper: Any, width: Double, height: Double)
|
|
34
|
+
|
|
35
|
+
// The only `StateWrapper` is RN's `StateWrapperImpl`, a fbjni `HybridData`. When the shadow node is
|
|
36
|
+
// destroyed its native pointer is reset and calling into it throws. Skip the update like RN does
|
|
37
|
+
// before its own native state accesses.
|
|
38
|
+
private fun isStateValid(stateWrapper: Any): Boolean = (stateWrapper as? HybridData)?.isValid == true
|
|
15
39
|
}
|
|
@@ -148,6 +148,7 @@ class ViewManagerWrapperDelegate(
|
|
|
148
148
|
|
|
149
149
|
fun onDestroy(view: View) {
|
|
150
150
|
try {
|
|
151
|
+
(view as? ComposeHostingView)?.disposeHostedComposition()
|
|
151
152
|
definition.onViewDestroys?.invoke(view)
|
|
152
153
|
} catch (exception: Throwable) {
|
|
153
154
|
// The view wasn't constructed correctly, so errors are expected.
|
|
@@ -177,3 +178,11 @@ class ViewManagerWrapperDelegate(
|
|
|
177
178
|
}
|
|
178
179
|
}
|
|
179
180
|
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Implemented by Compose-based views to dispose their composition when the
|
|
184
|
+
* view is destroyed.
|
|
185
|
+
*/
|
|
186
|
+
interface ComposeHostingView {
|
|
187
|
+
fun disposeHostedComposition()
|
|
188
|
+
}
|
|
@@ -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":"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;;;;OAIG;IACH,MAAM,UAAU,kBAAkB,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CACnE;AAGD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM,CAAC;QACf,UAAiB,UAAW,SAAQ,cAAc;SAAG;QACrD,UAAiB,OAAQ,SAAQ,WAAW;YAC1C,GAAG,EAAE,UAAU,CAAC;SACjB;KACF;IAED,IAAI,IAAI,EAAE,OAAO,UAAU,CAAC;IAC5B,IAAI,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC;IAE5B;;OAEG;IACH,IAAI,cAAc,EAAE,cAAc,CAAC;CACpC;AAED,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACrD,YAAY,EAAE,UAAU,EAAE,CAAC"}
|
|
@@ -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"}
|
|
@@ -670,8 +670,13 @@ public final class AppContext: NSObject, EXAppContextProtocol, @unchecked Sendab
|
|
|
670
670
|
if moduleRegistry.has(moduleWithName: "ExpoGo") {
|
|
671
671
|
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "EXReloadActiveAppRequest"), object: nil)
|
|
672
672
|
} else {
|
|
673
|
-
|
|
674
|
-
|
|
673
|
+
// Must run on the main thread (see #31789), but synchronously when already there — deferring
|
|
674
|
+
// via `DispatchQueue.main.async` deadlocks the bridgeless reload against TurboModule teardown.
|
|
675
|
+
let trigger = { RCTTriggerReloadCommandListeners(reason) }
|
|
676
|
+
if Thread.isMainThread {
|
|
677
|
+
trigger()
|
|
678
|
+
} else {
|
|
679
|
+
DispatchQueue.main.async(execute: trigger)
|
|
675
680
|
}
|
|
676
681
|
}
|
|
677
682
|
}
|
|
@@ -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.18",
|
|
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.10",
|
|
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": "b293744c7b199c6cbe910188863e93a174c21250",
|
|
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
|
}
|