expo-modules-core 57.0.20 → 57.0.21
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 +8 -0
- package/android/build.gradle +2 -2
- package/android/src/compose/expo/modules/kotlin/views/ExpoComposeView.kt +0 -10
- package/android/src/main/java/expo/modules/kotlin/views/ExpoView.kt +15 -1
- package/common/cpp/EventEmitter.cpp +10 -0
- package/ios/Core/AppContext.swift +20 -7
- package/ios/Platform/Platform.h +6 -1
- package/package.json +2 -2
- 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,14 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 57.0.21 — 2026-10-06
|
|
14
|
+
|
|
15
|
+
### 🐛 Bug fixes
|
|
16
|
+
|
|
17
|
+
- Fixed listeners removed with `subscription.remove()` never being garbage-collected, together with their emitter and everything their closure references, when the closure can reach the subscription (as in `useEvent`, `useEventListener` or a `useEffect` that returns `() => subscription.remove()`). ([#50603](https://github.com/expo/expo/pull/50603) by [@gkueny](https://github.com/gkueny))
|
|
18
|
+
- [iOS] Fixed a crash (`The app context has been lost`) when an Expo view mounts during a reload. ([#50721](https://github.com/expo/expo/pull/50721) by [@tsapeta](https://github.com/tsapeta))
|
|
19
|
+
- [Android] Fixed Expo views using Android layout not completing system-forced layout passes. ([#48679](https://github.com/expo/expo/pull/48679) by [@lujjjh](https://github.com/lujjjh))
|
|
20
|
+
|
|
13
21
|
## 57.0.20 — 2026-09-29
|
|
14
22
|
|
|
15
23
|
### 🐛 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 = '57.0.
|
|
30
|
+
version = '57.0.21'
|
|
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 "57.0.
|
|
97
|
+
versionName "57.0.21"
|
|
98
98
|
buildConfigField "String", "EXPO_MODULES_CORE_VERSION", "\"${versionName}\""
|
|
99
99
|
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", "true"
|
|
100
100
|
|
|
@@ -2,7 +2,6 @@ package expo.modules.kotlin.views
|
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.content.Context
|
|
5
|
-
import android.content.res.Configuration
|
|
6
5
|
import android.util.Log
|
|
7
6
|
import android.view.View
|
|
8
7
|
import android.view.ViewGroup
|
|
@@ -114,15 +113,6 @@ abstract class ExpoComposeView<T : ComposeProps>(
|
|
|
114
113
|
}
|
|
115
114
|
}
|
|
116
115
|
|
|
117
|
-
override fun dispatchConfigurationChanged(newConfig: Configuration) {
|
|
118
|
-
super.dispatchConfigurationChanged(newConfig)
|
|
119
|
-
// React Native owns this view's bounds and may not schedule another Android layout pass
|
|
120
|
-
// when a configuration change leaves its Yoga layout unchanged.
|
|
121
|
-
if (withHostingView && isAttachedToWindow && isLaidOut) {
|
|
122
|
-
requestLayout()
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
116
|
/**
|
|
127
117
|
* Validates that this non-hosting Compose view has a valid Compose parent.
|
|
128
118
|
*
|
|
@@ -35,7 +35,8 @@ abstract class ExpoView(
|
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* Manually trigger measure and layout.
|
|
38
|
-
* If [shouldUseAndroidLayout] is set to `true`, this method will be called automatically after
|
|
38
|
+
* If [shouldUseAndroidLayout] is set to `true`, this method will be called automatically after
|
|
39
|
+
* [requestLayout] or [forceLayout].
|
|
39
40
|
*/
|
|
40
41
|
@UiThread
|
|
41
42
|
fun measureAndLayout() {
|
|
@@ -54,6 +55,19 @@ abstract class ExpoView(
|
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
override fun forceLayout() {
|
|
59
|
+
super.forceLayout()
|
|
60
|
+
if (shouldUseAndroidLayout && isAttachedToWindow && isLaidOut) {
|
|
61
|
+
// ViewRootImpl can force-layout the hierarchy without React Native laying out its
|
|
62
|
+
// managed children. Preserve the Android-layout contract at that boundary.
|
|
63
|
+
post {
|
|
64
|
+
if (isAttachedToWindow && isLayoutRequested) {
|
|
65
|
+
measureAndLayout()
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
57
71
|
open fun clipToPaddingBox(canvas: Canvas) {
|
|
58
72
|
// When the border radius is set, we need to clip the content to the padding box.
|
|
59
73
|
// This is because the border radius is applied to the background drawable, not the view itself.
|
|
@@ -188,10 +188,20 @@ jsi::Value createEventSubscription(jsi::Runtime &runtime, const std::string &eve
|
|
|
188
188
|
std::shared_ptr<jsi::Value> listenerValue = std::make_shared<jsi::Value>(runtime, listener);
|
|
189
189
|
|
|
190
190
|
jsi::HostFunctionType removeSubscription = [eventName, emitterValue, listenerValue](jsi::Runtime &runtime, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value {
|
|
191
|
+
if (!listenerValue->isObject()) {
|
|
192
|
+
// The subscription has already been removed.
|
|
193
|
+
return jsi::Value::undefined();
|
|
194
|
+
}
|
|
191
195
|
jsi::Object emitter = emitterValue->getObject(runtime);
|
|
192
196
|
jsi::Function listener = listenerValue->getObject(runtime).getFunction(runtime);
|
|
193
197
|
|
|
194
198
|
removeListener(runtime, emitter, eventName, listener);
|
|
199
|
+
|
|
200
|
+
// Values held by this host function are GC roots. The listener's closure often references the subscription
|
|
201
|
+
// (e.g. a React effect returning `() => subscription.remove()`), so keeping them after the removal would form
|
|
202
|
+
// a cycle through a root that the garbage collector can never break, leaking the listener and the emitter.
|
|
203
|
+
*emitterValue = jsi::Value::undefined();
|
|
204
|
+
*listenerValue = jsi::Value::undefined();
|
|
195
205
|
return jsi::Value::undefined();
|
|
196
206
|
};
|
|
197
207
|
|
|
@@ -120,17 +120,30 @@ public final class AppContext: NSObject, EXAppContextProtocol, @unchecked Sendab
|
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
123
|
+
/// The number of app contexts created so far in this process.
|
|
124
|
+
private static let appContextsCount = Mutex<Int>(0)
|
|
125
|
+
|
|
126
|
+
/// The position of this app context in creation order, starting at 0.
|
|
127
|
+
internal let appIndex: Int = AppContext.appContextsCount.withLock { count in
|
|
128
|
+
defer {
|
|
129
|
+
count += 1
|
|
130
|
+
}
|
|
131
|
+
return count
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/// The application identifier that distinguishes app contexts that are alive at the same time,
|
|
135
|
+
/// for example during a reload or when more than one `RCTHost` is running. It's `nil` for the first
|
|
136
|
+
/// app context, so its view names have no suffix.
|
|
128
137
|
@objc
|
|
129
138
|
public var appIdentifier: String? {
|
|
130
|
-
|
|
139
|
+
return AppContext.appIdentifier(forIndex: appIndex)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
internal static func appIdentifier(forIndex index: Int) -> String? {
|
|
143
|
+
if index == 0 {
|
|
131
144
|
return nil
|
|
132
145
|
}
|
|
133
|
-
return
|
|
146
|
+
return String(index)
|
|
134
147
|
}
|
|
135
148
|
|
|
136
149
|
/**
|
package/ios/Platform/Platform.h
CHANGED
|
@@ -18,7 +18,12 @@
|
|
|
18
18
|
@compatibility_alias UIImage NSImage;
|
|
19
19
|
@compatibility_alias UIImageView NSImageView;
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
// `@compatibility_alias` doesn't define a preprocessor macro, so `#ifndef
|
|
22
|
+
// UIApplication` never detected that `React/RCTUIKit.h` (imported above)
|
|
23
|
+
// already declares this same alias. Under Swift Explicit Modules, that
|
|
24
|
+
// duplicate declaration becomes a real Clang conflict. Guard on the
|
|
25
|
+
// header's presence instead.
|
|
26
|
+
#if !__has_include(<React/RCTUIKit.h>)
|
|
22
27
|
@compatibility_alias UIApplication NSApplication;
|
|
23
28
|
#endif
|
|
24
29
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-modules-core",
|
|
3
|
-
"version": "57.0.
|
|
3
|
+
"version": "57.0.21",
|
|
4
4
|
"description": "The core of Expo Modules architecture",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -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": "50ac74f1ab5682c8e32b24da6e5e97dd4dbbb80f",
|
|
70
70
|
"scripts": {
|
|
71
71
|
"build": "expo-module build",
|
|
72
72
|
"clean": "expo-module clean",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|