expo-modules-core 55.0.5 → 55.0.7

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.
Files changed (33) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/compose/expo/modules/kotlin/views/ExpoComposeView.kt +14 -5
  4. package/android/src/compose/expo/modules/kotlin/views/ModuleDefinitionBuilderComposeExtension.kt +2 -2
  5. package/android/src/main/cpp/MethodMetadata.cpp +1 -1
  6. package/android/src/main/cpp/MethodMetadata.h +5 -0
  7. package/android/src/main/cpp/concepts/jni.h +43 -0
  8. package/android/src/main/cpp/concepts/jni_deref.h +39 -0
  9. package/android/src/main/cpp/concepts/jsi.h +20 -0
  10. package/android/src/main/cpp/decorators/JSDecoratorsBridgingObject.cpp +12 -2
  11. package/android/src/main/cpp/decorators/JSDecoratorsBridgingObject.h +1 -0
  12. package/android/src/main/cpp/decorators/JSFunctionsDecorator.cpp +4 -0
  13. package/android/src/main/cpp/decorators/JSFunctionsDecorator.h +3 -0
  14. package/android/src/main/cpp/types/CppType.h +2 -1
  15. package/android/src/main/cpp/types/JNIToJSIConverter.cpp +61 -0
  16. package/android/src/main/cpp/types/JNIToJSIConverter.h +128 -407
  17. package/android/src/main/cpp/types/ReturnType.h +33 -0
  18. package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +23 -8
  19. package/android/src/main/java/expo/modules/kotlin/functions/SyncFunctionComponent.kt +1 -0
  20. package/android/src/main/java/expo/modules/kotlin/jni/ReturnType.kt +30 -0
  21. package/android/src/main/java/expo/modules/kotlin/jni/decorators/JSDecoratorsBridgingObject.kt +1 -0
  22. package/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt +67 -0
  23. package/android/src/main/java/expo/modules/kotlin/types/ExperimentalJSTypeConverter.kt +237 -0
  24. package/android/src/main/java/expo/modules/kotlin/types/ReturnType.kt +3 -158
  25. package/ios/Core/Views/SwiftUI/SwiftUIHostingView.swift +38 -23
  26. package/ios/Core/Views/SwiftUI/SwiftUIViewProps.swift +6 -1
  27. package/ios/Core/Worklets/Worklet.swift +15 -0
  28. package/ios/Fabric/ExpoFabricViewObjC.mm +3 -0
  29. package/ios/Platform/Platform.swift +18 -0
  30. package/ios/Worklets/WorkletExecutor.h +14 -0
  31. package/ios/Worklets/WorkletExecutor.mm +72 -0
  32. package/package.json +2 -2
  33. package/types.d.ts +2 -0
package/CHANGELOG.md CHANGED
@@ -10,6 +10,26 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 55.0.7 — 2026-02-03
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - Fix global type declaration chain to point to `expo -> expo-modules-core/types -> ./build/global` rather than `types="node"` ([#42751](https://github.com/expo/expo/pull/42751) by [@kitten](https://github.com/kitten))
18
+
19
+ ## 55.0.6 — 2026-02-03
20
+
21
+ ### 🛠 Breaking changes
22
+
23
+ - [iOS] Renamed `ignoreSafeAreaKeyboardInsets` to `ignoreSafeArea` on `Host` component. It now accepts `'all'` or `'keyboard'` instead of a boolean. ([#42598](https://github.com/expo/expo/pull/42598) by [@nishan](https://github.com/intergalacticspacehighway))
24
+
25
+ ### 🎉 New features
26
+
27
+ - [macOS] Added `NSEdgeInsets.zero` extension to better match `UIEdgeInsets`. ([#42675](https://github.com/expo/expo/pull/42675) by [@tsapeta](https://github.com/tsapeta))
28
+
29
+ ### 🐛 Bug fixes
30
+
31
+ - [iOS] Fixed a crash in Fabric when unmounting a view while a geometry change event is being dispatched. ([#42628](https://github.com/expo/expo/issues/42628) by [@danishshaik](https://github.com/danishshaik)) ([#42634](https://github.com/expo/expo/pull/42634) by [@danishshaik](https://github.com/danishshaik))
32
+
13
33
  ## 55.0.5 — 2026-01-27
14
34
 
15
35
  _This version does not introduce any user-facing changes._
@@ -31,6 +51,7 @@ _This version does not introduce any user-facing changes._
31
51
  ### 💡 Others
32
52
 
33
53
  - [macos] Remove react-native-macos@0.79.0 workarounds ([#42409](https://github.com/expo/expo/pull/42409) by [@gabrieldonadel](https://github.com/gabrieldonadel))
54
+ - Improved Jetpack Compose integration for Expo UI. ([#42450](https://github.com/expo/expo/pull/42450) by [@kudo](https://github.com/kudo))
34
55
 
35
56
  ## 55.0.1 — 2026-01-22
36
57
 
@@ -29,7 +29,7 @@ if (shouldIncludeCompose) {
29
29
  }
30
30
 
31
31
  group = 'host.exp.exponent'
32
- version = '55.0.5'
32
+ version = '55.0.7'
33
33
 
34
34
  def isExpoModulesCoreTests = {
35
35
  Gradle gradle = getGradle()
@@ -96,7 +96,7 @@ android {
96
96
  defaultConfig {
97
97
  consumerProguardFiles 'proguard-rules.pro'
98
98
  versionCode 1
99
- versionName "55.0.5"
99
+ versionName "55.0.7"
100
100
  buildConfigField "String", "EXPO_MODULES_CORE_VERSION", "\"${versionName}\""
101
101
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", "true"
102
102
 
@@ -9,7 +9,6 @@ import androidx.compose.foundation.layout.RowScope
9
9
  import androidx.compose.runtime.Composable
10
10
  import androidx.compose.runtime.getValue
11
11
  import androidx.compose.runtime.mutableStateOf
12
- import androidx.compose.runtime.setValue
13
12
  import androidx.compose.ui.platform.ComposeView
14
13
  import androidx.compose.ui.platform.ViewCompositionStrategy
15
14
  import androidx.core.view.size
@@ -142,7 +141,18 @@ abstract class ExpoComposeView<T : ComposeProps>(
142
141
  }
143
142
  }
144
143
 
145
- class ExpoViewComposableScope(val view: ComposeFunctionHolder<*>) {
144
+ /**
145
+ * A composable DSL scope that wraps an [ExpoComposeView] to provide syntax sugar.
146
+ *
147
+ * This scope allows defining view content using a functional, DSL-style API
148
+ * without creating a dedicated subclass of [ExpoComposeView].
149
+ */
150
+ class FunctionalComposableScope(
151
+ val view: ComposeFunctionHolder<*>,
152
+ val composableScope: ComposableScope
153
+ ) {
154
+ val appContext = view.appContext
155
+
146
156
  @Composable
147
157
  fun Child(composableScope: ComposableScope, index: Int) {
148
158
  view.Child(composableScope, index)
@@ -167,16 +177,15 @@ class ComposeFunctionHolder<Props : ComposeProps>(
167
177
  context: Context,
168
178
  appContext: AppContext,
169
179
  override val name: String,
170
- private val composableContent: @Composable ExpoViewComposableScope.(props: Props) -> Unit,
180
+ private val composableContent: @Composable FunctionalComposableScope.(props: Props) -> Unit,
171
181
  override val props: Props
172
182
  ) : ExpoComposeView<Props>(context, appContext), ViewFunctionHolder {
173
183
  val propsMutableState = mutableStateOf(props)
174
- val scope = ExpoViewComposableScope(this)
175
184
 
176
185
  @Composable
177
186
  override fun ComposableScope.Content() {
178
187
  val props by propsMutableState
179
- with(scope) {
188
+ with(FunctionalComposableScope(this@ComposeFunctionHolder, this@Content)) {
180
189
  composableContent(props)
181
190
  }
182
191
  }
@@ -40,7 +40,7 @@ open class ModuleDefinitionBuilderWithCompose(
40
40
  inline fun <reified Props : ComposeProps> View(
41
41
  name: String,
42
42
  events: ComposeViewFunctionDefinitionBuilder<Props>.() -> Unit = {},
43
- noinline viewFunction: @Composable ExpoViewComposableScope.(props: Props) -> Unit
43
+ noinline viewFunction: @Composable FunctionalComposableScope.(props: Props) -> Unit
44
44
  ) {
45
45
  val definitionBuilder = ComposeViewFunctionDefinitionBuilder(name, Props::class, viewFunction)
46
46
  events.invoke(definitionBuilder)
@@ -52,7 +52,7 @@ open class ModuleDefinitionBuilderWithCompose(
52
52
  class ComposeViewFunctionDefinitionBuilder<Props : ComposeProps>(
53
53
  val name: String,
54
54
  val propsClass: KClass<Props>,
55
- val viewFunction: @Composable ExpoViewComposableScope.(props: Props) -> Unit
55
+ val viewFunction: @Composable FunctionalComposableScope.(props: Props) -> Unit
56
56
  ) {
57
57
  private var callbacksDefinition: CallbacksDefinition? = null
58
58
 
@@ -194,7 +194,7 @@ jsi::Value MethodMetadata::callSync(
194
194
  jni::JniLocalScope scope(env, (int) count);
195
195
 
196
196
  auto result = this->callJNISync(env, rt, thisValue, args, count);
197
- return convert(env, rt, result);
197
+ return convert(env, rt, this->info.returnType, result);
198
198
  }
199
199
 
200
200
  jsi::Function MethodMetadata::toAsyncFunction(
@@ -5,6 +5,7 @@
5
5
  #include "types/CppType.h"
6
6
  #include "types/ExpectedType.h"
7
7
  #include "types/AnyType.h"
8
+ #include "types/ReturnType.h"
8
9
 
9
10
  #include <jsi/jsi.h>
10
11
  #include <fbjni/fbjni.h>
@@ -48,6 +49,10 @@ public:
48
49
  * Representation of expected argument types.
49
50
  */
50
51
  std::vector<std::unique_ptr<AnyType>> argTypes;
52
+ /**
53
+ * Representation of expected return type.
54
+ */
55
+ ReturnType returnType = ReturnType::UNKNOWN;
51
56
  };
52
57
 
53
58
  Info info;
@@ -0,0 +1,43 @@
1
+ // Copyright © 2026-present 650 Industries, Inc. (aka Expo)
2
+
3
+ #pragma once
4
+
5
+ #include <concepts>
6
+ #include <fbjni/fbjni.h>
7
+ #include "jni_deref.h"
8
+
9
+ namespace jni = facebook::jni;
10
+
11
+ namespace expo {
12
+
13
+ template<typename T>
14
+ concept HasCthis = requires(T &t) { t->cthis(); };
15
+
16
+ template<typename T>
17
+ concept HasToStdString = requires(T &t) { t->toStdString(); };
18
+
19
+ template<typename T>
20
+ concept HasValue = requires(T &t) { t->value(); };
21
+
22
+ template<typename T>
23
+ concept HasGetRegion = requires(T &t, jsize s) { t->getRegion(s, s); };
24
+
25
+ template<typename T>
26
+ concept IsJBoolean = std::is_same_v<jni_deref_t<T>, jni::JBoolean>;
27
+
28
+ template<typename T>
29
+ concept JniRef =
30
+ std::is_same_v<T, jni::local_ref<jni_deref_t<T>>> ||
31
+ std::is_same_v<T, jni::global_ref<jni_deref_t<T>>> ||
32
+ std::is_same_v<T, jni::alias_ref<jni_deref_t<T>>>;
33
+
34
+ template<typename T, typename Inner>
35
+ concept JniRefTo = JniRef<T> && std::is_same_v<jni_deref_t<T>, Inner>;
36
+
37
+ template<typename T, typename Inner>
38
+ concept JCollectionRef = JniRefTo<T, jni::JCollection<Inner>>;
39
+
40
+ template<typename T, typename Key, typename Value>
41
+ concept JMapRef = JniRefTo<T, jni::JMap<Key, Value>>;
42
+
43
+ } // namespace expo
@@ -0,0 +1,39 @@
1
+ // Copyright © 2026-present 650 Industries, Inc. (aka Expo)
2
+
3
+ #pragma once
4
+
5
+ #include <concepts>
6
+ #include <fbjni/fbjni.h>
7
+
8
+ namespace jni = facebook::jni;
9
+
10
+ namespace expo::jni_deref_impl {
11
+
12
+ template<typename T>
13
+ struct deref {
14
+ using type = T;
15
+ };
16
+
17
+ template<typename T>
18
+ struct deref<jni::local_ref<T>> {
19
+ using type = T;
20
+ };
21
+
22
+ template<typename T>
23
+ struct deref<jni::global_ref<T>> {
24
+ using type = T;
25
+ };
26
+
27
+ template<typename T>
28
+ struct deref<jni::alias_ref<T>> {
29
+ using type = T;
30
+ };
31
+
32
+ } // namespace expo::jni_deref_impl
33
+
34
+ namespace expo {
35
+
36
+ template<typename T>
37
+ using jni_deref_t = typename jni_deref_impl::deref<std::remove_cvref_t<T>>::type;
38
+
39
+ } // namespace expo
@@ -0,0 +1,20 @@
1
+ // Copyright © 2026-present 650 Industries, Inc. (aka Expo)
2
+
3
+ #pragma once
4
+
5
+ #include <concepts>
6
+ #include <jsi/jsi.h>
7
+
8
+ namespace jsi = facebook::jsi;
9
+
10
+ namespace expo {
11
+
12
+ template<typename T>
13
+ concept TriviallyConvertibleToJSI = std::is_constructible_v<jsi::Value, T>;
14
+
15
+ template<typename T>
16
+ concept ConvertibleToJSI =
17
+ std::is_constructible_v<jsi::Value, jsi::Runtime &, T> &&
18
+ !std::is_constructible_v<jsi::Value, T>;
19
+
20
+ } // namespace expo
@@ -3,6 +3,7 @@
3
3
  #include "JSDecoratorsBridgingObject.h"
4
4
 
5
5
  #include "JSClassesDecorator.h"
6
+ #include "../types/ReturnType.h"
6
7
 
7
8
  namespace expo {
8
9
 
@@ -36,13 +37,21 @@ void JSDecoratorsBridgingObject::registerSyncFunction(
36
37
  jboolean takesOwner,
37
38
  jboolean enumerable,
38
39
  jni::alias_ref<jni::JArrayClass<ExpectedType>> expectedArgTypes,
40
+ jint cppReturnType,
39
41
  jni::alias_ref<JNIFunctionBody::javaobject> body
40
42
  ) {
41
43
  if (!functionDecorator) {
42
44
  functionDecorator = std::make_unique<JSFunctionsDecorator>();
43
45
  }
44
46
 
45
- functionDecorator->registerSyncFunction(name, takesOwner, enumerable, expectedArgTypes, body);
47
+ functionDecorator->registerSyncFunction(
48
+ name,
49
+ takesOwner,
50
+ enumerable,
51
+ expectedArgTypes,
52
+ (ReturnType)cppReturnType,
53
+ body
54
+ );
46
55
  }
47
56
 
48
57
  void JSDecoratorsBridgingObject::registerAsyncFunction(
@@ -94,7 +103,8 @@ void JSDecoratorsBridgingObject::registerConstant(
94
103
  constantsDecorator->registerConstant(name, getter);
95
104
  }
96
105
 
97
- void JSDecoratorsBridgingObject::registerConstants(jni::alias_ref<react::NativeMap::javaobject> constants) {
106
+ void JSDecoratorsBridgingObject::registerConstants(
107
+ jni::alias_ref<react::NativeMap::javaobject> constants) {
98
108
  if (!constantsDecorator) {
99
109
  constantsDecorator = std::make_unique<JSConstantsDecorator>();
100
110
  }
@@ -51,6 +51,7 @@ public:
51
51
  jboolean takesOwner,
52
52
  jboolean enumerable,
53
53
  jni::alias_ref<jni::JArrayClass<ExpectedType>> expectedArgTypes,
54
+ jint cppReturnType,
54
55
  jni::alias_ref<JNIFunctionBody::javaobject> body
55
56
  );
56
57
 
@@ -30,6 +30,7 @@ void JSFunctionsDecorator::registerFunction(
30
30
  bool enumerable,
31
31
  bool isAsync,
32
32
  std::vector<std::unique_ptr<AnyType>> &&argTypes,
33
+ ReturnType returnType,
33
34
  jni::global_ref<jobject> body
34
35
  ) {
35
36
  MethodMetadata::Info info{
@@ -51,6 +52,7 @@ void JSFunctionsDecorator::registerSyncFunction(
51
52
  jboolean takesOwner,
52
53
  jboolean enumerable,
53
54
  jni::alias_ref<jni::JArrayClass<ExpectedType>> expectedArgTypes,
55
+ ReturnType returnType,
54
56
  jni::alias_ref<JNIFunctionBody::javaobject> body
55
57
  ) {
56
58
  registerFunction(
@@ -61,6 +63,7 @@ void JSFunctionsDecorator::registerSyncFunction(
61
63
  enumerable,
62
64
  /*isAsync=*/false,
63
65
  mapConverters(expectedArgTypes),
66
+ returnType,
64
67
  jni::make_global(body)
65
68
  );
66
69
  }
@@ -80,6 +83,7 @@ void JSFunctionsDecorator::registerAsyncFunction(
80
83
  enumerable,
81
84
  /*isAsync=*/true,
82
85
  mapConverters(expectedArgTypes),
86
+ ReturnType::UNKNOWN,
83
87
  jni::make_global(body)
84
88
  );
85
89
  }
@@ -12,6 +12,7 @@
12
12
  #include "../MethodMetadata.h"
13
13
  #include "../JNIFunctionBody.h"
14
14
  #include "../types/ExpectedType.h"
15
+ #include "../types/ReturnType.h"
15
16
 
16
17
  namespace jni = facebook::jni;
17
18
  namespace jsi = facebook::jsi;
@@ -26,6 +27,7 @@ public:
26
27
  jboolean takesOwner,
27
28
  jboolean enumerable,
28
29
  jni::alias_ref<jni::JArrayClass<ExpectedType>> expectedArgTypes,
30
+ ReturnType returnType,
29
31
  jni::alias_ref<JNIFunctionBody::javaobject> body
30
32
  );
31
33
 
@@ -56,6 +58,7 @@ private:
56
58
  bool enumerable,
57
59
  bool isAsync,
58
60
  std::vector<std::unique_ptr<AnyType>> &&argTypes,
61
+ ReturnType returnType,
59
62
  jni::global_ref<jobject> body
60
63
  );
61
64
  };
@@ -7,7 +7,7 @@ namespace expo {
7
7
  * A cpp version of the `expo.modules.kotlin.jni.CppType` enum.
8
8
  * Used to determine which representation of the js value should be sent to the Kotlin.
9
9
  */
10
- enum CppType {
10
+ enum class CppType {
11
11
  NONE = 0,
12
12
  DOUBLE = 1 << 0,
13
13
  INT = 1 << 1,
@@ -35,4 +35,5 @@ enum CppType {
35
35
  NATIVE_ARRAY_BUFFER = 1 << 23,
36
36
  SERIALIZABLE = 1 << 24,
37
37
  };
38
+
38
39
  } // namespace expo
@@ -90,6 +90,67 @@ jsi::Value convert(
90
90
  return jsi::Value::undefined();
91
91
  }
92
92
 
93
+ jsi::Value convert(
94
+ JNIEnv *env,
95
+ jsi::Runtime &rt,
96
+ ReturnType returnType,
97
+ const jni::local_ref<jobject> &value
98
+ ) {
99
+ #define CAST_AND_RETURN(type) \
100
+ return convertToJS(env, rt, *((jni::local_ref<type>*)((void*)&value)));
101
+ #define COMMA ,
102
+
103
+ switch (returnType) {
104
+ case ReturnType::UNKNOWN:
105
+ return convert(env, rt, value);
106
+ case ReturnType::DOUBLE:
107
+ CAST_AND_RETURN(jni::JDouble)
108
+ case ReturnType::INT:
109
+ CAST_AND_RETURN(jni::JInteger)
110
+ case ReturnType::LONG:
111
+ CAST_AND_RETURN(jni::JLong)
112
+ case ReturnType::STRING:
113
+ CAST_AND_RETURN(jni::JString)
114
+ case ReturnType::BOOLEAN:
115
+ CAST_AND_RETURN(jni::JBoolean)
116
+ case ReturnType::FLOAT:
117
+ CAST_AND_RETURN(jni::JFloat)
118
+ case ReturnType::WRITEABLE_ARRAY:
119
+ CAST_AND_RETURN(react::WritableNativeArray::javaobject)
120
+ case ReturnType::WRITEABLE_MAP:
121
+ CAST_AND_RETURN(react::WritableNativeMap::javaobject)
122
+ case ReturnType::JS_MODULE:
123
+ CAST_AND_RETURN(JavaScriptModuleObject::javaobject)
124
+ case ReturnType::SHARED_OBJECT:
125
+ CAST_AND_RETURN(JSharedObject::javaobject)
126
+ case ReturnType::JS_TYPED_ARRAY:
127
+ CAST_AND_RETURN(JavaScriptTypedArray::javaobject)
128
+ case ReturnType::JS_ARRAY_BUFFER:
129
+ CAST_AND_RETURN(JavaScriptArrayBuffer::javaobject)
130
+ case ReturnType::NATIVE_ARRAY_BUFFER:
131
+ CAST_AND_RETURN(NativeArrayBuffer::javaobject)
132
+ case ReturnType::MAP:
133
+ CAST_AND_RETURN(jni::JMap<jstring COMMA jobject>)
134
+ case ReturnType::COLLECTION:
135
+ CAST_AND_RETURN(jni::JCollection<jobject>)
136
+ case ReturnType::DOUBLE_ARRAY:
137
+ CAST_AND_RETURN(jni::JArrayDouble)
138
+ case ReturnType::INT_ARRAY:
139
+ CAST_AND_RETURN(jni::JArrayInt)
140
+ case ReturnType::LONG_ARRAY:
141
+ CAST_AND_RETURN(jni::JArrayLong)
142
+ case ReturnType::FLOAT_ARRAY:
143
+ CAST_AND_RETURN(jni::JArrayFloat)
144
+ case ReturnType::BOOLEAN_ARRAY:
145
+ CAST_AND_RETURN(jni::JArrayBoolean )
146
+ }
147
+
148
+ #undef COMMA
149
+ #undef CAST_AND_RETURN
150
+
151
+ assert("Unhandled ReturnType in JNIToJSIConverter::convert" && false);
152
+ }
153
+
93
154
  std::optional<jsi::Value> decorateValueForDynamicExtension(jsi::Runtime &rt, const jsi::Value &value) {
94
155
  if (value.isString()) {
95
156
  std::string string = value.getString(rt).utf8(rt);