expo-modules-core 56.0.16 → 56.0.17
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 +7 -0
- package/android/build.gradle +2 -2
- package/android/src/compose/expo/modules/kotlin/views/ExpoComposeView.kt +49 -7
- package/android/src/main/cpp/fabric/NativeStatePropsGetter.cpp +2 -2
- 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/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/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,12 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 56.0.17 — 2026-06-15
|
|
14
|
+
|
|
15
|
+
### 🐛 Bug fixes
|
|
16
|
+
|
|
17
|
+
- [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))
|
|
18
|
+
|
|
13
19
|
## 56.0.16 — 2026-06-10
|
|
14
20
|
|
|
15
21
|
### 🎉 New features
|
|
@@ -19,6 +25,7 @@
|
|
|
19
25
|
### 🐛 Bug fixes
|
|
20
26
|
|
|
21
27
|
- [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))
|
|
28
|
+
- [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
29
|
- [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
30
|
- [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
31
|
|
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.17'
|
|
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.17"
|
|
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
|
/**
|
|
@@ -191,22 +199,56 @@ abstract class ExpoComposeView<T : ComposeProps>(
|
|
|
191
199
|
|
|
192
200
|
private fun addComposeView() {
|
|
193
201
|
val composeView = ComposeView(context).also {
|
|
202
|
+
// Give each Host a unique id so its rememberSaveable state gets its own key.
|
|
203
|
+
// All Hosts share the Activity's SavedStateRegistry (set below), so without an id
|
|
204
|
+
// they'd collide on one key and only the first could save/restore state.
|
|
205
|
+
it.id = generateViewId()
|
|
194
206
|
it.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
|
195
|
-
it
|
|
207
|
+
// Pin the composition to the Activity lifecycle so it survives
|
|
208
|
+
// react-native-screens detaching inactive screens on every switch.
|
|
209
|
+
// The strategy alone isn't enough: Compose's WrappedComposition also
|
|
210
|
+
// observes the view-tree lifecycle owner found at first attach — the
|
|
211
|
+
// screen fragment's, which RN-screens destroys per switch — and
|
|
212
|
+
// self-disposes on its ON_DESTROY, leaving a dead composition that
|
|
213
|
+
// never recreates. Overriding the owners on the ComposeView (nearest
|
|
214
|
+
// tag wins) points both at the Activity. Unmount disposes explicitly
|
|
215
|
+
// via disposeHostedComposition().
|
|
216
|
+
val activity = appContext.currentActivity
|
|
217
|
+
if (activity is LifecycleOwner && activity is SavedStateRegistryOwner) {
|
|
218
|
+
it.setViewTreeLifecycleOwner(activity)
|
|
219
|
+
it.setViewTreeSavedStateRegistryOwner(activity)
|
|
220
|
+
it.setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnLifecycleDestroyed(activity.lifecycle))
|
|
221
|
+
} else {
|
|
222
|
+
// No Activity to pin to: keep the prior behavior, including the
|
|
223
|
+
// dispose-on-reattach workaround for blank compositions after
|
|
224
|
+
// navigation (https://github.com/expo/expo/pull/34689).
|
|
225
|
+
it.setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
|
226
|
+
it.addOnAttachStateChangeListener(
|
|
227
|
+
OnAttachAfterDetachmentListener(onAttachAfterDetachment = {
|
|
228
|
+
it.disposeComposition()
|
|
229
|
+
})
|
|
230
|
+
)
|
|
231
|
+
}
|
|
196
232
|
it.setContent {
|
|
197
233
|
with(ComposableScope()) {
|
|
198
234
|
Content()
|
|
199
235
|
}
|
|
200
236
|
}
|
|
201
|
-
it.addOnAttachStateChangeListener(
|
|
202
|
-
OnAttachAfterDetachmentListener(onAttachAfterDetachment = {
|
|
203
|
-
it.disposeComposition()
|
|
204
|
-
})
|
|
205
|
-
)
|
|
206
237
|
}
|
|
238
|
+
hostingComposeView = composeView
|
|
207
239
|
addView(composeView)
|
|
208
240
|
}
|
|
209
241
|
|
|
242
|
+
override fun disposeHostedComposition() {
|
|
243
|
+
hostingComposeView?.let {
|
|
244
|
+
// disposeComposition() alone leaves the composition strategy's lifecycle observer
|
|
245
|
+
// registered on the Activity, which leaks this view.
|
|
246
|
+
// Swapping the strategy first detaches that observer, then we dispose.
|
|
247
|
+
it.setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnDetachedFromWindow)
|
|
248
|
+
it.disposeComposition()
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
210
252
|
override fun addView(child: View, index: Int, params: ViewGroup.LayoutParams) {
|
|
211
253
|
val view = if (child !is ExpoComposeView<*> && child !is ComposeView && this !is RNHostViewInterface) {
|
|
212
254
|
ExpoComposeAndroidView(child, appContext)
|
|
@@ -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
|
|
|
@@ -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
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-modules-core",
|
|
3
|
-
"version": "56.0.
|
|
3
|
+
"version": "56.0.17",
|
|
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": "812dc007aefed0c432c0439fdfe05ee2f4f21da2",
|
|
70
70
|
"scripts": {
|
|
71
71
|
"build": "expo-module build",
|
|
72
72
|
"clean": "expo-module clean",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|