expo-modules-core 3.0.5 → 3.0.6

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 CHANGED
@@ -10,6 +10,12 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 3.0.6 — 2025-08-25
14
+
15
+ ### 🎉 New features
16
+
17
+ - [Android] Add new type - `ValueOrUndefined`. ([#39116](https://github.com/expo/expo/pull/39116) by [@lukmccall](https://github.com/lukmccall))
18
+
13
19
  ## 3.0.5 — 2025-08-21
14
20
 
15
21
  _This version does not introduce any user-facing changes._
@@ -25,7 +25,7 @@ if (shouldIncludeCompose) {
25
25
  }
26
26
 
27
27
  group = 'host.exp.exponent'
28
- version = '3.0.5'
28
+ version = '3.0.6'
29
29
 
30
30
  def isExpoModulesCoreTests = {
31
31
  Gradle gradle = getGradle()
@@ -75,7 +75,7 @@ android {
75
75
  defaultConfig {
76
76
  consumerProguardFiles 'proguard-rules.pro'
77
77
  versionCode 1
78
- versionName "3.0.5"
78
+ versionName "3.0.6"
79
79
  buildConfigField "String", "EXPO_MODULES_CORE_VERSION", "\"${versionName}\""
80
80
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled.toString()
81
81
 
@@ -54,6 +54,8 @@ JCache::JCache(JNIEnv *env) {
54
54
  jJavaScriptModuleObject = REGISTER_CLASS("expo/modules/kotlin/jni/JavaScriptModuleObject");
55
55
 
56
56
  #undef REGISTER_CLASS
57
+
58
+ jUndefined = getJUndefined(env);
57
59
  }
58
60
 
59
61
  std::shared_ptr<JCache> JCacheHolder::jCache = nullptr;
@@ -106,4 +108,11 @@ void JCache::unLoad(JNIEnv *env) {
106
108
  env->DeleteGlobalRef(jJavaScriptModuleObject);
107
109
  }
108
110
 
111
+ jobject JCache::getJUndefined(JNIEnv *env) {
112
+ jclass clazz = env->FindClass("expo/modules/kotlin/types/ValueOrUndefined");
113
+ jmethodID methodId = env->GetStaticMethodID(clazz, "getUndefined", "()Ljava/lang/Object;");
114
+ return env->NewGlobalRef(
115
+ env->CallStaticObjectMethod(clazz, methodId)
116
+ );
117
+ }
109
118
  } // namespace expo
@@ -57,8 +57,12 @@ public:
57
57
  jclass jSharedObject;
58
58
  jclass jJavaScriptModuleObject;
59
59
 
60
+ jobject jUndefined;
61
+
60
62
  void unLoad(JNIEnv *env);
61
63
  private:
64
+ static jobject getJUndefined(JNIEnv *env);
65
+
62
66
  std::unordered_map<std::string, jclass> jClassRegistry;
63
67
  };
64
68
 
@@ -30,5 +30,6 @@ enum CppType {
30
30
  JS_FUNCTION = 1 << 18,
31
31
  ANY = 1 << 19,
32
32
  NULLABLE = 1 << 20,
33
+ VALUE_OR_UNDEFINED = 1 << 21,
33
34
  };
34
35
  } // namespace expo
@@ -648,4 +648,32 @@ jobject NullableFrontendConverter::convert(
648
648
  return parameterConverter->convert(rt, env, value);
649
649
  }
650
650
 
651
+ ValueOrUndefinedFrontendConverter::ValueOrUndefinedFrontendConverter(
652
+ jni::local_ref<SingleType::javaobject> expectedType
653
+ ) : parameterConverter(
654
+ FrontendConverterProvider::instance()->obtainConverter(
655
+ expectedType->getFirstParameterType()
656
+ )
657
+ ) {}
658
+
659
+ bool ValueOrUndefinedFrontendConverter::canConvert(
660
+ jsi::Runtime &rt,
661
+ const jsi::Value &value
662
+ ) const {
663
+ return value.isUndefined() ||
664
+ parameterConverter->canConvert(rt, value);
665
+ }
666
+
667
+ jobject ValueOrUndefinedFrontendConverter::convert(
668
+ jsi::Runtime &rt,
669
+ JNIEnv *env,
670
+ const jsi::Value &value
671
+ ) const {
672
+ if (value.isUndefined()) {
673
+ return env->NewLocalRef(JCacheHolder::get().jUndefined);
674
+ }
675
+
676
+ return parameterConverter->convert(rt, env, value);
677
+ }
678
+
651
679
  } // namespace expo
@@ -438,4 +438,21 @@ public:
438
438
  private:
439
439
  std::shared_ptr<FrontendConverter> parameterConverter;
440
440
  };
441
+
442
+ class ValueOrUndefinedFrontendConverter : public FrontendConverter {
443
+ public:
444
+ ValueOrUndefinedFrontendConverter(
445
+ jni::local_ref<jni::JavaClass<SingleType>::javaobject> expectedType
446
+ );
447
+
448
+ jobject convert(
449
+ jsi::Runtime &rt,
450
+ JNIEnv *env,
451
+ const jsi::Value &value
452
+ ) const override;
453
+
454
+ bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const override;
455
+ private:
456
+ std::shared_ptr<FrontendConverter> parameterConverter;
457
+ };
441
458
  } // namespace expo
@@ -74,6 +74,10 @@ std::shared_ptr<FrontendConverter> FrontendConverterProvider::obtainConverter(
74
74
  return std::make_shared<MapFrontendConverter>(expectedType->getFirstType());
75
75
  }
76
76
 
77
+ if (combinedType == CppType::VALUE_OR_UNDEFINED) {
78
+ return std::make_shared<ValueOrUndefinedFrontendConverter>(expectedType->getFirstType());
79
+ }
80
+
77
81
  std::vector<std::shared_ptr<FrontendConverter>> converters;
78
82
  auto singleTypes = expectedType->getPossibleTypes();
79
83
  size_t size = singleTypes->size();
@@ -3,6 +3,7 @@ package expo.modules.kotlin.jni
3
3
  import com.facebook.react.bridge.ReadableArray
4
4
  import com.facebook.react.bridge.ReadableMap
5
5
  import expo.modules.kotlin.typedarray.TypedArray
6
+ import expo.modules.kotlin.types.ValueOrUndefined
6
7
  import kotlin.reflect.KClass
7
8
 
8
9
  private var nextValue = 0
@@ -34,5 +35,6 @@ enum class CppType(val clazz: KClass<*>, val value: Int = nextValue()) {
34
35
  SHARED_OBJECT_ID(Int::class),
35
36
  JS_FUNCTION(JavaScriptFunction::class),
36
37
  ANY(Any::class),
37
- NULLABLE(Any::class)
38
+ NULLABLE(Any::class),
39
+ VALUE_OR_UNDEFINED(ValueOrUndefined::class)
38
40
  }
@@ -159,6 +159,10 @@ object TypeConverterProviderImpl : TypeConverterProvider {
159
159
  return JavaScriptFunctionTypeConverter<Any>(type)
160
160
  }
161
161
 
162
+ if (ValueOrUndefined::class.java.isAssignableFrom(jClass)) {
163
+ return ValueOrUndefinedTypeConverter(this, type)
164
+ }
165
+
162
166
  return handelEither(type, jClass)
163
167
  ?: throw MissingTypeConverter(type)
164
168
  }
@@ -0,0 +1,25 @@
1
+ package expo.modules.kotlin.types
2
+
3
+ import expo.modules.core.interfaces.DoNotStrip
4
+
5
+ @DoNotStrip
6
+ sealed interface ValueOrUndefined<T> {
7
+ val isUndefined: Boolean
8
+ get() = this is Undefined
9
+
10
+ val optional: T?
11
+ get() = when (this) {
12
+ is Value -> value
13
+ is Undefined -> null
14
+ }
15
+
16
+ data class Value<T>(val value: T) : ValueOrUndefined<T>
17
+ object Undefined : ValueOrUndefined<Nothing>
18
+
19
+ companion object {
20
+ // helper function that can be used from C++ to get the instance of Undefined
21
+ @JvmStatic
22
+ @DoNotStrip
23
+ fun getUndefined(): Any = Undefined
24
+ }
25
+ }
@@ -0,0 +1,38 @@
1
+ package expo.modules.kotlin.types
2
+
3
+ import expo.modules.kotlin.AppContext
4
+ import expo.modules.kotlin.jni.CppType
5
+ import expo.modules.kotlin.jni.ExpectedType
6
+ import expo.modules.kotlin.jni.SingleType
7
+ import kotlin.reflect.KType
8
+
9
+ class ValueOrUndefinedTypeConverter(
10
+ converterProvider: TypeConverterProvider,
11
+ innerType: KType
12
+ ) : TypeConverter<ValueOrUndefined<*>> {
13
+ private val innerTypeConverter = converterProvider.obtainTypeConverter(
14
+ requireNotNull(innerType.arguments.first().type) {
15
+ "The ValueOrUndefined type should contain the argument type."
16
+ }
17
+ )
18
+
19
+ override fun convert(value: Any?, context: AppContext?, forceConversion: Boolean): ValueOrUndefined<*>? {
20
+ return if (value is ValueOrUndefined.Undefined) {
21
+ ValueOrUndefined.Undefined
22
+ } else {
23
+ val converterValue = innerTypeConverter.convert(value, context)
24
+ ValueOrUndefined.Value(converterValue)
25
+ }
26
+ }
27
+
28
+ override fun getCppRequiredTypes(): ExpectedType {
29
+ return ExpectedType(
30
+ SingleType(
31
+ CppType.VALUE_OR_UNDEFINED,
32
+ arrayOf(innerTypeConverter.getCppRequiredTypes())
33
+ )
34
+ )
35
+ }
36
+
37
+ override fun isTrivial() = false
38
+ }
@@ -181,7 +181,7 @@ static std::unordered_map<std::string, expo::ExpoViewComponentDescriptor::Flavor
181
181
  // when a VirtualView inserted to a standard UIView.
182
182
  // We use this call point here for sanity check.
183
183
  @throw [NSException exceptionWithName:@"SwiftUIVirtualViewException"
184
- reason:@"A SwiftUI view is inserted as a child of a standard UIView. Please check that you have wrapped the SwiftUI view in an WithHostingView."
184
+ reason:@"A SwiftUI view is inserted as a child of a standard UIView. Double check that in JSX you have wrapped your component with `<Host>` from '@expo/ui/swift-ui'."
185
185
  userInfo:nil];
186
186
  return NO;
187
187
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-modules-core",
3
- "version": "3.0.5",
3
+ "version": "3.0.6",
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
  "devDependencies": {
49
49
  "@testing-library/react-native": "^13.2.0",
50
- "expo-module-scripts": "^5.0.2"
50
+ "expo-module-scripts": "^5.0.3"
51
51
  },
52
- "gitHead": "3275774c9286477adba9bc226d3923a996217ee4"
52
+ "gitHead": "ef0b9ecf9645d3e93587d5ee5030dbfcbf735bbd"
53
53
  }