expo-modules-core 2.3.12 → 2.4.0

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 (37) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/cpp/JSharedObject.cpp +1 -1
  4. package/android/src/main/java/expo/modules/kotlin/classcomponent/ClassComponentBuilder.kt +22 -11
  5. package/android/src/main/java/expo/modules/kotlin/events/EventsDefinition.kt +9 -1
  6. package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +6 -0
  7. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionBuilder.kt +28 -24
  8. package/android/src/main/java/expo/modules/kotlin/jni/ExpectedType.kt +122 -5
  9. package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +6 -1
  10. package/android/src/main/java/expo/modules/kotlin/modules/ModuleConvertersBuilder.kt +50 -0
  11. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +31 -7
  12. package/android/src/main/java/expo/modules/kotlin/objects/ObjectDefinitionBuilder.kt +28 -30
  13. package/android/src/main/java/expo/modules/kotlin/objects/ObjectDefinitionData.kt +15 -0
  14. package/android/src/main/java/expo/modules/kotlin/traits/SavableTrait.kt +57 -0
  15. package/android/src/main/java/expo/modules/kotlin/traits/Trait.kt +8 -0
  16. package/android/src/main/java/expo/modules/kotlin/types/AnyType.kt +9 -0
  17. package/android/src/main/java/expo/modules/kotlin/types/EitherTypeConverter.kt +3 -3
  18. package/android/src/main/java/expo/modules/kotlin/types/ExpoDynamic.kt +57 -0
  19. package/android/src/main/java/expo/modules/kotlin/types/TypeConverterCollection.kt +81 -0
  20. package/android/src/main/java/expo/modules/kotlin/views/ConcreteViewProp.kt +18 -3
  21. package/android/src/main/java/expo/modules/kotlin/views/ViewDefinitionBuilder.kt +40 -25
  22. package/ios/Api/Factories/ViewFactories.swift +16 -0
  23. package/ios/Core/Conversions.swift +51 -7
  24. package/ios/Core/Convertibles/Convertibles+Color.swift +1 -1
  25. package/ios/Core/DynamicTypes/DynamicSharedObjectType.swift +25 -14
  26. package/ios/Core/Functions/AsyncFunctionDefinition.swift +1 -1
  27. package/ios/Core/Promise.swift +0 -11
  28. package/ios/Core/Views/ConcreteViewProp.swift +16 -0
  29. package/ios/Core/Views/SwiftUI/Convertibles+SwiftUI.swift +62 -0
  30. package/ios/Core/Views/SwiftUI/SwiftUIHostingView.swift +7 -0
  31. package/ios/DevTools/ExpoRequestInterceptorProtocol.swift +40 -0
  32. package/ios/DevTools/ModuleDefinitionEncoder.swift +182 -0
  33. package/ios/DevTools/URLSessionSessionDelegateProxy.swift +17 -0
  34. package/ios/ReactDelegates/ExpoReactDelegate.swift +2 -2
  35. package/ios/ReactDelegates/ExpoReactDelegateHandler.swift +1 -1
  36. package/ios/Tests/FunctionSpec.swift +27 -1
  37. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -10,6 +10,32 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 2.4.0 — 2025-06-04
14
+
15
+ ### 🎉 New features
16
+
17
+ - [iOS] Add module registry encoder for JSON. ([#36677](https://github.com/expo/expo/pull/36677) by [@aleqsio](https://github.com/aleqsio))
18
+ - [Android] New custom Type Converter API ([#36823](https://github.com/expo/expo/pull/36823) by [@jakex7](https://github.com/jakex7))
19
+ - [iOS] Natively support `rgb` color conversions. ([#36914](https://github.com/expo/expo/pull/36914) by [@alanjhughes](https://github.com/alanjhughes))
20
+ - Add default values to prop definition. ([#37011](https://github.com/expo/expo/pull/37011), [#37013](https://github.com/expo/expo/pull/37013) by [@jakex7](https://github.com/jakex7))
21
+
22
+ ### 🐛 Bug fixes
23
+
24
+ - [iOS] Call `createRootViewController` from the `ExpoReactNativeFactoryDelegate`. ([#36787](https://github.com/expo/expo/pull/36787) by [@alanjhughes](https://github.com/alanjhughes))
25
+ - [iOS] Prevent crash on failed networked requests ([#36896](https://github.com/expo/expo/pull/36896) by [@adrum](https://github.com/adrum))
26
+
27
+ ### 💡 Others
28
+
29
+ - Improved `@expo/ui/swift-ui-primitives` integrations. ([#36937](https://github.com/expo/expo/pull/36937) by [@kudo](https://github.com/kudo))
30
+ - [iOS] Use dynamic type for AsyncFunction result conversion ([#36986](https://github.com/expo/expo/pull/36986) by [@jakex7](https://github.com/jakex7))
31
+
32
+ ## 2.3.13 — 2025-05-08
33
+
34
+ ### 🐛 Bug fixes
35
+
36
+ - [Android] Fix `getSharedObjectId` for devices running android 7 and below. ([#36698](https://github.com/expo/expo/pull/36698) by [@lukmccall](https://github.com/lukmccall))
37
+ - [iOS] Fixed missing upload progress from `XMLHttpRequest` when network inspector is enabled. ([#36715](https://github.com/expo/expo/pull/36715) by [@kudo](https://github.com/kudo))
38
+
13
39
  ## 2.3.12 — 2025-04-30
14
40
 
15
41
  ### 🐛 Bug fixes
@@ -25,7 +25,7 @@ if (shouldIncludeCompose) {
25
25
  }
26
26
 
27
27
  group = 'host.exp.exponent'
28
- version = '2.3.12'
28
+ version = '2.4.0'
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 "2.3.12"
78
+ versionName "2.4.0"
79
79
  buildConfigField "String", "EXPO_MODULES_CORE_VERSION", "\"${versionName}\""
80
80
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled.toString()
81
81
 
@@ -5,7 +5,7 @@
5
5
  namespace expo {
6
6
 
7
7
  int JSharedObject::getId() noexcept {
8
- static const auto method = getClass()->getMethod<int()>("getSharedObjectId");
8
+ static const auto method = javaClassStatic()->getMethod<int()>("getSharedObjectId");
9
9
  return method(self());
10
10
  }
11
11
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  package expo.modules.kotlin.classcomponent
4
4
 
5
+ import expo.modules.kotlin.AppContext
5
6
  import expo.modules.kotlin.component6
6
7
  import expo.modules.kotlin.component7
7
8
  import expo.modules.kotlin.component8
@@ -10,7 +11,9 @@ import expo.modules.kotlin.objects.ObjectDefinitionBuilder
10
11
  import expo.modules.kotlin.objects.PropertyComponentBuilderWithThis
11
12
  import expo.modules.kotlin.sharedobjects.SharedObject
12
13
  import expo.modules.kotlin.sharedobjects.SharedRef
14
+ import expo.modules.kotlin.traits.Trait
13
15
  import expo.modules.kotlin.types.AnyType
16
+ import expo.modules.kotlin.types.TypeConverterProvider
14
17
  import expo.modules.kotlin.types.enforceType
15
18
  import expo.modules.kotlin.types.toAnyType
16
19
  import expo.modules.kotlin.types.toArgsArray
@@ -19,11 +22,14 @@ import kotlin.reflect.KClass
19
22
  import kotlin.reflect.full.isSubclassOf
20
23
 
21
24
  class ClassComponentBuilder<SharedObjectType : Any>(
25
+ private val appContext: AppContext,
22
26
  val name: String,
23
27
  private val ownerClass: KClass<SharedObjectType>,
24
- val ownerType: AnyType
25
- ) : ObjectDefinitionBuilder() {
28
+ val ownerType: AnyType,
29
+ converters: TypeConverterProvider? = null
30
+ ) : ObjectDefinitionBuilder(converters) {
26
31
  var constructor: SyncFunctionComponent? = null
32
+ val traits = mutableListOf<Trait<in SharedObjectType>>()
27
33
 
28
34
  fun buildClass(): ClassDefinitionData {
29
35
  val hasOwnerType = ownerClass != Unit::class
@@ -43,7 +49,8 @@ class ClassComponentBuilder<SharedObjectType : Any>(
43
49
  }
44
50
  }
45
51
 
46
- val objectData = buildObject()
52
+ val objectData = buildObject() + traits.map { t -> t.export(appContext) }.reduceOrNull { t1, t2 -> t1 + t2 }
53
+
47
54
  objectData.functions.forEach {
48
55
  it.ownerType = ownerType.kType
49
56
  it.canTakeOwner = true
@@ -70,6 +77,10 @@ class ClassComponentBuilder<SharedObjectType : Any>(
70
77
  )
71
78
  }
72
79
 
80
+ fun UseTrait(trait: Trait<in SharedObjectType>) {
81
+ traits.add(trait)
82
+ }
83
+
73
84
  inline fun Constructor(
74
85
  crossinline body: () -> SharedObjectType
75
86
  ): SyncFunctionComponent {
@@ -82,7 +93,7 @@ class ClassComponentBuilder<SharedObjectType : Any>(
82
93
  inline fun <reified P0> Constructor(
83
94
  crossinline body: (p0: P0) -> SharedObjectType
84
95
  ): SyncFunctionComponent {
85
- return SyncFunctionComponent("constructor", toArgsArray<P0>(), toReturnType<Any>()) { (p0) ->
96
+ return SyncFunctionComponent("constructor", toArgsArray<P0>(converterProvider = converters), toReturnType<Any>()) { (p0) ->
86
97
  enforceType<P0>(p0)
87
98
  body(p0)
88
99
  }.also {
@@ -93,7 +104,7 @@ class ClassComponentBuilder<SharedObjectType : Any>(
93
104
  inline fun <reified P0, reified P1> Constructor(
94
105
  crossinline body: (p0: P0, p1: P1) -> SharedObjectType
95
106
  ): SyncFunctionComponent {
96
- return SyncFunctionComponent("constructor", toArgsArray<P0, P1>(), toReturnType<Any>()) { (p0, p1) ->
107
+ return SyncFunctionComponent("constructor", toArgsArray<P0, P1>(converterProvider = converters), toReturnType<Any>()) { (p0, p1) ->
97
108
  enforceType<P0, P1>(p0, p1)
98
109
  body(p0, p1)
99
110
  }.also {
@@ -104,7 +115,7 @@ class ClassComponentBuilder<SharedObjectType : Any>(
104
115
  inline fun <reified P0, reified P1, reified P2> Constructor(
105
116
  crossinline body: (p0: P0, p1: P1, p2: P2) -> SharedObjectType
106
117
  ): SyncFunctionComponent {
107
- return SyncFunctionComponent("constructor", toArgsArray<P0, P1, P2>(), toReturnType<Any>()) { (p0, p1, p2) ->
118
+ return SyncFunctionComponent("constructor", toArgsArray<P0, P1, P2>(converterProvider = converters), toReturnType<Any>()) { (p0, p1, p2) ->
108
119
  enforceType<P0, P1, P2>(p0, p1, p2)
109
120
  body(p0, p1, p2)
110
121
  }.also {
@@ -115,7 +126,7 @@ class ClassComponentBuilder<SharedObjectType : Any>(
115
126
  inline fun <reified P0, reified P1, reified P2, reified P3> Constructor(
116
127
  crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3) -> SharedObjectType
117
128
  ): SyncFunctionComponent {
118
- return SyncFunctionComponent("constructor", toArgsArray<P0, P1, P2, P3>(), toReturnType<Any>()) { (p0, p1, p2, p3) ->
129
+ return SyncFunctionComponent("constructor", toArgsArray<P0, P1, P2, P3>(converterProvider = converters), toReturnType<Any>()) { (p0, p1, p2, p3) ->
119
130
  enforceType<P0, P1, P2, P3>(p0, p1, p2, p3)
120
131
  body(p0, p1, p2, p3)
121
132
  }.also {
@@ -126,7 +137,7 @@ class ClassComponentBuilder<SharedObjectType : Any>(
126
137
  inline fun <reified P0, reified P1, reified P2, reified P3, reified P4> Constructor(
127
138
  crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4) -> SharedObjectType
128
139
  ): SyncFunctionComponent {
129
- return SyncFunctionComponent("constructor", toArgsArray<P0, P1, P2, P3, P4>(), toReturnType<Any>()) { (p0, p1, p2, p3, p4) ->
140
+ return SyncFunctionComponent("constructor", toArgsArray<P0, P1, P2, P3, P4>(converterProvider = converters), toReturnType<Any>()) { (p0, p1, p2, p3, p4) ->
130
141
  enforceType<P0, P1, P2, P3, P4>(p0, p1, p2, p3, p4)
131
142
  body(p0, p1, p2, p3, p4)
132
143
  }.also {
@@ -137,7 +148,7 @@ class ClassComponentBuilder<SharedObjectType : Any>(
137
148
  inline fun <reified P0, reified P1, reified P2, reified P3, reified P4, reified P5> Constructor(
138
149
  crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) -> SharedObjectType
139
150
  ): SyncFunctionComponent {
140
- return SyncFunctionComponent("constructor", toArgsArray<P0, P1, P2, P3, P4, P5>(), toReturnType<Any>()) { (p0, p1, p2, p3, p4, p5) ->
151
+ return SyncFunctionComponent("constructor", toArgsArray<P0, P1, P2, P3, P4, P5>(converterProvider = converters), toReturnType<Any>()) { (p0, p1, p2, p3, p4, p5) ->
141
152
  enforceType<P0, P1, P2, P3, P4, P5>(p0, p1, p2, p3, p4, p5)
142
153
  body(p0, p1, p2, p3, p4, p5)
143
154
  }.also {
@@ -148,7 +159,7 @@ class ClassComponentBuilder<SharedObjectType : Any>(
148
159
  inline fun <reified P0, reified P1, reified P2, reified P3, reified P4, reified P5, reified P6> Constructor(
149
160
  crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6) -> SharedObjectType
150
161
  ): SyncFunctionComponent {
151
- return SyncFunctionComponent("constructor", toArgsArray<P0, P1, P2, P3, P4, P5, P6>(), toReturnType<Any>()) { (p0, p1, p2, p3, p4, p5, p6) ->
162
+ return SyncFunctionComponent("constructor", toArgsArray<P0, P1, P2, P3, P4, P5, P6>(converterProvider = converters), toReturnType<Any>()) { (p0, p1, p2, p3, p4, p5, p6) ->
152
163
  enforceType<P0, P1, P2, P3, P4, P5, P6>(p0, p1, p2, p3, p4, p5, p6)
153
164
  body(p0, p1, p2, p3, p4, p5, p6)
154
165
  }.also {
@@ -159,7 +170,7 @@ class ClassComponentBuilder<SharedObjectType : Any>(
159
170
  inline fun <reified P0, reified P1, reified P2, reified P3, reified P4, reified P5, reified P6, reified P7> Constructor(
160
171
  crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7) -> SharedObjectType
161
172
  ): SyncFunctionComponent {
162
- return SyncFunctionComponent("constructor", toArgsArray<P0, P1, P2, P3, P4, P5, P6, P7>(), toReturnType<Any>()) { (p0, p1, p2, p3, p4, p5, p6, p7) ->
173
+ return SyncFunctionComponent("constructor", toArgsArray<P0, P1, P2, P3, P4, P5, P6, P7>(converterProvider = converters), toReturnType<Any>()) { (p0, p1, p2, p3, p4, p5, p6, p7) ->
163
174
  enforceType<P0, P1, P2, P3, P4, P5, P6, P7>(p0, p1, p2, p3, p4, p5, p6, p7)
164
175
  body(p0, p1, p2, p3, p4, p5, p6, p7)
165
176
  }.also {
@@ -1,3 +1,11 @@
1
1
  package expo.modules.kotlin.events
2
2
 
3
- class EventsDefinition(val names: Array<out String>)
3
+ class EventsDefinition(val names: Array<String>) {
4
+ operator fun plus(other: EventsDefinition?): EventsDefinition {
5
+ if (other == null) {
6
+ return this
7
+ }
8
+
9
+ return EventsDefinition(names + other.names)
10
+ }
11
+ }
@@ -93,6 +93,12 @@ internal class MissingTypeConverter(
93
93
  message = "Cannot find type converter for '$forType'. Make sure the class implements `expo.modules.kotlin.records.Record` (i.e. `class MyObj : Record`)."
94
94
  )
95
95
 
96
+ internal class InvalidExpectedType(
97
+ forType: KType
98
+ ) : CodedException(
99
+ message = "Cannot obtain ExpectedType form '$forType'."
100
+ )
101
+
96
102
  @DoNotStrip
97
103
  internal class InvalidArgsNumberException(received: Int, expected: Int, required: Int = expected) :
98
104
  CodedException(
@@ -6,10 +6,14 @@ import expo.modules.kotlin.component6
6
6
  import expo.modules.kotlin.component7
7
7
  import expo.modules.kotlin.component8
8
8
  import expo.modules.kotlin.Promise
9
+ import expo.modules.kotlin.types.TypeConverterProvider
9
10
  import expo.modules.kotlin.types.enforceType
10
11
  import expo.modules.kotlin.types.toArgsArray
11
12
 
12
- class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
13
+ class AsyncFunctionBuilder(
14
+ @PublishedApi internal val name: String,
15
+ @PublishedApi internal val converters: TypeConverterProvider? = null
16
+ ) {
13
17
  @PublishedApi
14
18
  internal var asyncFunctionComponent: BaseAsyncFunctionComponent? = null
15
19
 
@@ -20,7 +24,7 @@ class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
20
24
  }
21
25
 
22
26
  inline fun <reified R, reified P0> SuspendBody(crossinline block: suspend (p0: P0) -> R): SuspendFunctionComponent {
23
- return SuspendFunctionComponent(name, toArgsArray<P0>()) { (p0) ->
27
+ return SuspendFunctionComponent(name, toArgsArray<P0>(converterProvider = converters)) { (p0) ->
24
28
  enforceType<P0>(p0)
25
29
  block(p0)
26
30
  }.also {
@@ -29,7 +33,7 @@ class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
29
33
  }
30
34
 
31
35
  inline fun <reified R, reified P0, reified P1> SuspendBody(crossinline block: suspend (p0: P0, p1: P1) -> R): SuspendFunctionComponent {
32
- return SuspendFunctionComponent(name, toArgsArray<P0, P1>()) { (p0, p1) ->
36
+ return SuspendFunctionComponent(name, toArgsArray<P0, P1>(converterProvider = converters)) { (p0, p1) ->
33
37
  enforceType<P0, P1>(p0, p1)
34
38
  block(p0, p1)
35
39
  }.also {
@@ -38,7 +42,7 @@ class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
38
42
  }
39
43
 
40
44
  inline fun <reified R, reified P0, reified P1, reified P2> SuspendBody(crossinline block: suspend (p0: P0, p1: P1, p2: P2) -> R): SuspendFunctionComponent {
41
- return SuspendFunctionComponent(name, toArgsArray<P0, P1, P2>()) { (p0, p1, p2) ->
45
+ return SuspendFunctionComponent(name, toArgsArray<P0, P1, P2>(converterProvider = converters)) { (p0, p1, p2) ->
42
46
  enforceType<P0, P1, P2>(p0, p1, p2)
43
47
  block(p0, p1, p2)
44
48
  }.also {
@@ -47,7 +51,7 @@ class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
47
51
  }
48
52
 
49
53
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3> SuspendBody(crossinline block: suspend (p0: P0, p1: P1, p2: P2, p3: P3) -> R): SuspendFunctionComponent {
50
- return SuspendFunctionComponent(name, toArgsArray<P0, P1, P2, P3>()) { (p0, p1, p2, p3) ->
54
+ return SuspendFunctionComponent(name, toArgsArray<P0, P1, P2, P3>(converterProvider = converters)) { (p0, p1, p2, p3) ->
51
55
  enforceType<P0, P1, P2, P3>(p0, p1, p2, p3)
52
56
  block(p0, p1, p2, p3)
53
57
  }.also {
@@ -56,7 +60,7 @@ class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
56
60
  }
57
61
 
58
62
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4> SuspendBody(crossinline block: suspend (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4) -> R): SuspendFunctionComponent {
59
- return SuspendFunctionComponent(name, toArgsArray<P0, P1, P2, P3, P4>()) { (p0, p1, p2, p3, p4) ->
63
+ return SuspendFunctionComponent(name, toArgsArray<P0, P1, P2, P3, P4>(converterProvider = converters)) { (p0, p1, p2, p3, p4) ->
60
64
  enforceType<P0, P1, P2, P3, P4>(p0, p1, p2, p3, p4)
61
65
  block(p0, p1, p2, p3, p4)
62
66
  }.also {
@@ -65,7 +69,7 @@ class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
65
69
  }
66
70
 
67
71
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5> SuspendBody(crossinline block: suspend (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) -> R): SuspendFunctionComponent {
68
- return SuspendFunctionComponent(name, toArgsArray<P0, P1, P2, P3, P4, P5>()) { (p0, p1, p2, p3, p4, p5) ->
72
+ return SuspendFunctionComponent(name, toArgsArray<P0, P1, P2, P3, P4, P5>(converterProvider = converters)) { (p0, p1, p2, p3, p4, p5) ->
69
73
  enforceType<P0, P1, P2, P3, P4, P5>(p0, p1, p2, p3, p4, p5)
70
74
  block(p0, p1, p2, p3, p4, p5)
71
75
  }.also {
@@ -74,7 +78,7 @@ class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
74
78
  }
75
79
 
76
80
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5, reified P6> SuspendBody(crossinline block: suspend (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6) -> R): SuspendFunctionComponent {
77
- return SuspendFunctionComponent(name, toArgsArray<P0, P1, P2, P3, P4, P5, P6>()) { (p0, p1, p2, p3, p4, p5, p6) ->
81
+ return SuspendFunctionComponent(name, toArgsArray<P0, P1, P2, P3, P4, P5, P6>(converterProvider = converters)) { (p0, p1, p2, p3, p4, p5, p6) ->
78
82
  enforceType<P0, P1, P2, P3, P4, P5, P6>(p0, p1, p2, p3, p4, p5, p6)
79
83
  block(p0, p1, p2, p3, p4, p5, p6)
80
84
  }.also {
@@ -83,7 +87,7 @@ class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
83
87
  }
84
88
 
85
89
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5, reified P6, reified P7> SuspendBody(crossinline block: suspend (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7) -> R): SuspendFunctionComponent {
86
- return SuspendFunctionComponent(name, toArgsArray<P0, P1, P2, P3, P4, P5, P6, P7>()) { (p0, p1, p2, p3, p4, p5, p6, p7) ->
90
+ return SuspendFunctionComponent(name, toArgsArray<P0, P1, P2, P3, P4, P5, P6, P7>(converterProvider = converters)) { (p0, p1, p2, p3, p4, p5, p6, p7) ->
87
91
  enforceType<P0, P1, P2, P3, P4, P5, P6, P7>(p0, p1, p2, p3, p4, p5, p6, p7)
88
92
  block(p0, p1, p2, p3, p4, p5, p6, p7)
89
93
  }.also {
@@ -108,7 +112,7 @@ class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
108
112
  return if (P0::class == Promise::class) {
109
113
  AsyncFunctionWithPromiseComponent(name, emptyArray()) { _, promise -> body(promise as P0) }
110
114
  } else {
111
- createAsyncFunctionComponent(name, toArgsArray<P0>()) { (p0) ->
115
+ createAsyncFunctionComponent(name, toArgsArray<P0>(converterProvider = converters)) { (p0) ->
112
116
  enforceType<P0>(p0)
113
117
  body(p0)
114
118
  }
@@ -118,7 +122,7 @@ class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
118
122
  }
119
123
 
120
124
  inline fun <reified R, reified P0, reified P1> AsyncBody(crossinline body: (p0: P0, p1: P1) -> R): AsyncFunctionComponent {
121
- return createAsyncFunctionComponent(name, toArgsArray<P0, P1>()) { (p0, p1) ->
125
+ return createAsyncFunctionComponent(name, toArgsArray<P0, P1>(converterProvider = converters)) { (p0, p1) ->
122
126
  enforceType<P0, P1>(p0, p1)
123
127
  body(p0, p1)
124
128
  }.also {
@@ -128,7 +132,7 @@ class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
128
132
 
129
133
  @JvmName("AsyncFunctionWithPromise")
130
134
  inline fun <reified R, reified P0> AsyncBody(crossinline body: (p0: P0, p1: Promise) -> R): AsyncFunctionComponent {
131
- return AsyncFunctionWithPromiseComponent(name, toArgsArray<P0>()) { (p0), promise ->
135
+ return AsyncFunctionWithPromiseComponent(name, toArgsArray<P0>(converterProvider = converters)) { (p0), promise ->
132
136
  enforceType<P0>(p0)
133
137
  body(p0, promise)
134
138
  }.also {
@@ -137,7 +141,7 @@ class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
137
141
  }
138
142
 
139
143
  inline fun <reified R, reified P0, reified P1, reified P2> AsyncBody(crossinline body: (p0: P0, p1: P1, p2: P2) -> R): AsyncFunctionComponent {
140
- return createAsyncFunctionComponent(name, toArgsArray<P0, P1, P2>()) { (p0, p1, p2) ->
144
+ return createAsyncFunctionComponent(name, toArgsArray<P0, P1, P2>(converterProvider = converters)) { (p0, p1, p2) ->
141
145
  enforceType<P0, P1, P2>(p0, p1, p2)
142
146
  body(p0, p1, p2)
143
147
  }.also {
@@ -147,7 +151,7 @@ class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
147
151
 
148
152
  @JvmName("AsyncFunctionWithPromise")
149
153
  inline fun <reified R, reified P0, reified P1> AsyncBody(crossinline body: (p0: P0, p1: P1, p2: Promise) -> R): AsyncFunctionComponent {
150
- return AsyncFunctionWithPromiseComponent(name, toArgsArray<P0, P1>()) { (p0, p1), promise ->
154
+ return AsyncFunctionWithPromiseComponent(name, toArgsArray<P0, P1>(converterProvider = converters)) { (p0, p1), promise ->
151
155
  enforceType<P0, P1>(p0, p1)
152
156
  body(p0, p1, promise)
153
157
  }.also {
@@ -156,7 +160,7 @@ class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
156
160
  }
157
161
 
158
162
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3> AsyncBody(crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3) -> R): AsyncFunctionComponent {
159
- return createAsyncFunctionComponent(name, toArgsArray<P0, P1, P2, P3>()) { (p0, p1, p2, p3) ->
163
+ return createAsyncFunctionComponent(name, toArgsArray<P0, P1, P2, P3>(converterProvider = converters)) { (p0, p1, p2, p3) ->
160
164
  enforceType<P0, P1, P2, P3>(p0, p1, p2, p3)
161
165
  body(p0, p1, p2, p3)
162
166
  }.also {
@@ -166,7 +170,7 @@ class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
166
170
 
167
171
  @JvmName("AsyncFunctionWithPromise")
168
172
  inline fun <reified R, reified P0, reified P1, reified P2> AsyncBody(crossinline body: (p0: P0, p1: P1, p2: P2, p3: Promise) -> R): AsyncFunctionComponent {
169
- return AsyncFunctionWithPromiseComponent(name, toArgsArray<P0, P1, P2>()) { (p0, p1, p2), promise ->
173
+ return AsyncFunctionWithPromiseComponent(name, toArgsArray<P0, P1, P2>(converterProvider = converters)) { (p0, p1, p2), promise ->
170
174
  enforceType<P0, P1, P2>(p0, p1, p2)
171
175
  body(p0, p1, p2, promise)
172
176
  }.also {
@@ -175,7 +179,7 @@ class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
175
179
  }
176
180
 
177
181
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4> AsyncBody(crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4) -> R): AsyncFunctionComponent {
178
- return createAsyncFunctionComponent(name, toArgsArray<P0, P1, P2, P3, P4>()) { (p0, p1, p2, p3, p4) ->
182
+ return createAsyncFunctionComponent(name, toArgsArray<P0, P1, P2, P3, P4>(converterProvider = converters)) { (p0, p1, p2, p3, p4) ->
179
183
  enforceType<P0, P1, P2, P3, P4>(p0, p1, p2, p3, p4)
180
184
  body(p0, p1, p2, p3, p4)
181
185
  }.also {
@@ -185,7 +189,7 @@ class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
185
189
 
186
190
  @JvmName("AsyncFunctionWithPromise")
187
191
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3> AsyncBody(crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: Promise) -> R): AsyncFunctionComponent {
188
- return AsyncFunctionWithPromiseComponent(name, toArgsArray<P0, P1, P2, P3>()) { (p0, p1, p2, p3), promise ->
192
+ return AsyncFunctionWithPromiseComponent(name, toArgsArray<P0, P1, P2, P3>(converterProvider = converters)) { (p0, p1, p2, p3), promise ->
189
193
  enforceType<P0, P1, P2, P3>(p0, p1, p2, p3)
190
194
  body(p0, p1, p2, p3, promise)
191
195
  }.also {
@@ -194,7 +198,7 @@ class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
194
198
  }
195
199
 
196
200
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5> AsyncBody(crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) -> R): AsyncFunctionComponent {
197
- return createAsyncFunctionComponent(name, toArgsArray<P0, P1, P2, P3, P4, P5>()) { (p0, p1, p2, p3, p4, p5) ->
201
+ return createAsyncFunctionComponent(name, toArgsArray<P0, P1, P2, P3, P4, P5>(converterProvider = converters)) { (p0, p1, p2, p3, p4, p5) ->
198
202
  enforceType<P0, P1, P2, P3, P4, P5>(p0, p1, p2, p3, p4, p5)
199
203
  body(p0, p1, p2, p3, p4, p5)
200
204
  }.also {
@@ -204,7 +208,7 @@ class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
204
208
 
205
209
  @JvmName("AsyncFunctionWithPromise")
206
210
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4> AsyncBody(crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: Promise) -> R): AsyncFunctionComponent {
207
- return AsyncFunctionWithPromiseComponent(name, toArgsArray<P0, P1, P2, P3, P4>()) { (p0, p1, p2, p3, p4), promise ->
211
+ return AsyncFunctionWithPromiseComponent(name, toArgsArray<P0, P1, P2, P3, P4>(converterProvider = converters)) { (p0, p1, p2, p3, p4), promise ->
208
212
  enforceType<P0, P1, P2, P3, P4>(p0, p1, p2, p3, p4)
209
213
  body(p0, p1, p2, p3, p4, promise)
210
214
  }.also {
@@ -213,7 +217,7 @@ class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
213
217
  }
214
218
 
215
219
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5, reified P6> AsyncBody(crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6) -> R): AsyncFunctionComponent {
216
- return createAsyncFunctionComponent(name, toArgsArray<P0, P1, P2, P3, P4, P5, P6>()) { (p0, p1, p2, p3, p4, p5, p6) ->
220
+ return createAsyncFunctionComponent(name, toArgsArray<P0, P1, P2, P3, P4, P5, P6>(converterProvider = converters)) { (p0, p1, p2, p3, p4, p5, p6) ->
217
221
  enforceType<P0, P1, P2, P3, P4, P5, P6>(p0, p1, p2, p3, p4, p5, p6)
218
222
  body(p0, p1, p2, p3, p4, p5, p6)
219
223
  }.also {
@@ -223,7 +227,7 @@ class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
223
227
 
224
228
  @JvmName("AsyncFunctionWithPromise")
225
229
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5> AsyncBody(crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: Promise) -> R): AsyncFunctionComponent {
226
- return AsyncFunctionWithPromiseComponent(name, toArgsArray<P0, P1, P2, P3, P4, P5>()) { (p0, p1, p2, p3, p4, p5), promise ->
230
+ return AsyncFunctionWithPromiseComponent(name, toArgsArray<P0, P1, P2, P3, P4, P5>(converterProvider = converters)) { (p0, p1, p2, p3, p4, p5), promise ->
227
231
  enforceType<P0, P1, P2, P3, P4, P5>(p0, p1, p2, p3, p4, p5)
228
232
  body(p0, p1, p2, p3, p4, p5, promise)
229
233
  }.also {
@@ -232,7 +236,7 @@ class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
232
236
  }
233
237
 
234
238
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5, reified P6, reified P7> AsyncBody(crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7) -> R): AsyncFunctionComponent {
235
- return createAsyncFunctionComponent(name, toArgsArray<P0, P1, P2, P3, P4, P5, P6, P7>()) { (p0, p1, p2, p3, p4, p5, p6, p7) ->
239
+ return createAsyncFunctionComponent(name, toArgsArray<P0, P1, P2, P3, P4, P5, P6, P7>(converterProvider = converters)) { (p0, p1, p2, p3, p4, p5, p6, p7) ->
236
240
  enforceType<P0, P1, P2, P3, P4, P5, P6, P7>(p0, p1, p2, p3, p4, p5, p6, p7)
237
241
  body(p0, p1, p2, p3, p4, p5, p6, p7)
238
242
  }.also {
@@ -242,7 +246,7 @@ class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
242
246
 
243
247
  @JvmName("AsyncFunctionWithPromise")
244
248
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5, reified P6> AsyncBody(crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: Promise) -> R): AsyncFunctionComponent {
245
- return AsyncFunctionWithPromiseComponent(name, toArgsArray<P0, P1, P2, P3, P4, P5, P6>()) { (p0, p1, p2, p3, p4, p5, p6), promise ->
249
+ return AsyncFunctionWithPromiseComponent(name, toArgsArray<P0, P1, P2, P3, P4, P5, P6>(converterProvider = converters)) { (p0, p1, p2, p3, p4, p5, p6), promise ->
246
250
  enforceType<P0, P1, P2, P3, P4, P5, P6>(p0, p1, p2, p3, p4, p5, p6)
247
251
  body(p0, p1, p2, p3, p4, p5, p6, promise)
248
252
  }.also {
@@ -1,6 +1,9 @@
1
1
  package expo.modules.kotlin.jni
2
2
 
3
3
  import expo.modules.core.interfaces.DoNotStrip
4
+ import expo.modules.kotlin.exception.InvalidExpectedType
5
+ import kotlin.reflect.KClass
6
+ import kotlin.reflect.KType
4
7
 
5
8
  /**
6
9
  * A basic class that represents metadata about the expected type.
@@ -30,6 +33,66 @@ class SingleType(
30
33
  */
31
34
  @DoNotStrip
32
35
  fun getSecondParameterType() = parameterTypes?.get(1)
36
+
37
+ override fun equals(other: Any?): Boolean {
38
+ if (this === other) {
39
+ return true
40
+ }
41
+
42
+ if (javaClass != other?.javaClass) {
43
+ return false
44
+ }
45
+
46
+ other as SingleType
47
+
48
+ if (expectedCppType != other.expectedCppType) {
49
+ return false
50
+ }
51
+ if (!parameterTypes.contentEquals(other.parameterTypes)) {
52
+ return false
53
+ }
54
+
55
+ return true
56
+ }
57
+
58
+ override fun hashCode(): Int {
59
+ var result = expectedCppType.hashCode()
60
+ result = 31 * result + (parameterTypes?.contentHashCode() ?: 0)
61
+ return result
62
+ }
63
+
64
+ companion object {
65
+ fun merge(
66
+ first: SingleType,
67
+ second: SingleType
68
+ ): SingleType {
69
+ if (first.expectedCppType != second.expectedCppType) {
70
+ throw IllegalArgumentException(
71
+ "Cannot merge types with different CppType: ${first.expectedCppType} and ${second.expectedCppType}"
72
+ )
73
+ }
74
+
75
+ val firstTypeParameters = first.parameterTypes
76
+ val secondTypeParameters = second.parameterTypes
77
+ if (firstTypeParameters == null || secondTypeParameters == null) {
78
+ return first
79
+ }
80
+
81
+ require(firstTypeParameters.size == secondTypeParameters.size) {
82
+ "Cannot merge types with different number of parameters: ${first.parameterTypes.size} and ${second.parameterTypes.size}"
83
+ }
84
+
85
+ val size = firstTypeParameters.size
86
+ val parameters = (0..<size).map { index ->
87
+ ExpectedType.merge(firstTypeParameters[index], secondTypeParameters[index])
88
+ }
89
+
90
+ return SingleType(
91
+ first.expectedCppType,
92
+ parameters.toTypedArray()
93
+ )
94
+ }
95
+ }
33
96
  }
34
97
 
35
98
  /**
@@ -60,11 +123,25 @@ class ExpectedType(
60
123
  @DoNotStrip
61
124
  fun getFirstType() = innerPossibleTypes.first()
62
125
 
63
- operator fun plus(other: ExpectedType): ExpectedType {
64
- return ExpectedType(
65
- *this.innerPossibleTypes,
66
- *other.innerPossibleTypes
67
- )
126
+ override fun hashCode(): Int {
127
+ var result = innerCombinedTypes
128
+ result = 31 * result + innerPossibleTypes.contentHashCode()
129
+ return result
130
+ }
131
+
132
+ override operator fun equals(other: Any?): Boolean {
133
+ if (other !is ExpectedType) return false
134
+
135
+ if (this.innerPossibleTypes.size != other.innerPossibleTypes.size) return false
136
+ for (i in this.innerPossibleTypes.indices) {
137
+ if (this.innerPossibleTypes[i].expectedCppType != other.innerPossibleTypes[i].expectedCppType) {
138
+ return false
139
+ }
140
+ if (this.innerPossibleTypes[i].getFirstParameterType() != other.innerPossibleTypes[i].getFirstParameterType()) {
141
+ return false
142
+ }
143
+ }
144
+ return true
68
145
  }
69
146
 
70
147
  companion object {
@@ -96,5 +173,45 @@ class ExpectedType(
96
173
  fun forMap(valueType: ExpectedType) = ExpectedType(
97
174
  SingleType(CppType.MAP, arrayOf(valueType))
98
175
  )
176
+
177
+ fun fromKType(type: KType): ExpectedType {
178
+ val kClass = type.classifier as? KClass<*>
179
+ ?: throw IllegalArgumentException("Cannot obtain KClass from '$type'")
180
+ when (kClass) {
181
+ Int::class -> return ExpectedType(SingleType(CppType.INT))
182
+ Long::class -> return ExpectedType(SingleType(CppType.LONG))
183
+ Double::class -> return ExpectedType(SingleType(CppType.DOUBLE))
184
+ Float::class -> return ExpectedType(SingleType(CppType.FLOAT))
185
+ Boolean::class -> return ExpectedType(SingleType(CppType.BOOLEAN))
186
+ String::class -> return ExpectedType(SingleType(CppType.STRING))
187
+ }
188
+ if (kClass.java.isAssignableFrom(List::class.java)) {
189
+ val argType = type.arguments.firstOrNull()?.type
190
+ if (argType != null) {
191
+ return forList(fromKType(argType))
192
+ }
193
+ }
194
+ if (kClass.java.isAssignableFrom(Map::class.java)) {
195
+ val argType = type.arguments.getOrNull(1)?.type
196
+ if (argType != null) {
197
+ return forMap(fromKType(argType))
198
+ }
199
+ }
200
+ throw InvalidExpectedType(type)
201
+ }
202
+
203
+ fun merge(
204
+ vararg types: ExpectedType
205
+ ): ExpectedType {
206
+ val typesGroup = types
207
+ .flatMap { it.innerPossibleTypes.asIterable() }
208
+ .groupBy { it.expectedCppType }
209
+
210
+ val mergedTypes = typesGroup.map { (_, types) ->
211
+ types.reduce { a, b -> SingleType.merge(a, b) }
212
+ }
213
+
214
+ return ExpectedType(*mergedTypes.toTypedArray())
215
+ }
99
216
  }
100
217
  }
@@ -51,7 +51,7 @@ abstract class Module : AppContextProvider {
51
51
  moduleEventEmitter?.emit(convertEnumToString(enum), body)
52
52
  }
53
53
 
54
- open fun customConverterProvider(): TypeConverterProvider? = null
54
+ open fun converters(): TypeConverterProvider? = null
55
55
 
56
56
  abstract fun definition(): ModuleDefinitionData
57
57
  }
@@ -60,3 +60,8 @@ abstract class Module : AppContextProvider {
60
60
  inline fun Module.ModuleDefinition(crossinline block: ModuleDefinitionBuilder.() -> Unit): ModuleDefinitionData {
61
61
  return trace("${this.javaClass}.ModuleDefinition") { ModuleDefinitionBuilder(this).also(block).buildModule() }
62
62
  }
63
+
64
+ @Suppress("FunctionName")
65
+ inline fun Module.ModuleConverters(crossinline block: ModuleConvertersBuilder.() -> Unit): TypeConverterProvider {
66
+ return trace("${this.javaClass}.TypeConverters") { ModuleConvertersBuilder().also(block).buildTypeConverterProvider() }
67
+ }
@@ -0,0 +1,50 @@
1
+ @file:Suppress("FunctionName")
2
+
3
+ package expo.modules.kotlin.modules
4
+
5
+ import expo.modules.kotlin.exception.MissingTypeConverter
6
+ import expo.modules.kotlin.types.TypeConverter
7
+ import expo.modules.kotlin.types.TypeConverterComponent
8
+ import expo.modules.kotlin.types.TypeConverterProvider
9
+ import expo.modules.kotlin.types.lazyTypeOf
10
+ import kotlin.reflect.KClass
11
+ import kotlin.reflect.KType
12
+
13
+ class ModuleConvertersBuilder {
14
+ @PublishedApi
15
+ internal var convertersComponent = mutableListOf<TypeConverterComponent<*>>()
16
+
17
+ inline fun <reified T : Any> TypeConverter(classifier: KClass<T>): TypeConverterComponent<T> {
18
+ val converterComponent = TypeConverterComponent<T>(lazyTypeOf<T>(), lazyTypeOf<(T?)?>())
19
+ convertersComponent.add(converterComponent)
20
+ return converterComponent
21
+ }
22
+
23
+ inline fun <reified T : Any, reified P0 : Any> TypeConverter(
24
+ classifier: KClass<T>,
25
+ crossinline body: (p0: P0) -> T
26
+ ): TypeConverterComponent<T> {
27
+ return TypeConverter<T>(classifier).apply {
28
+ from<P0> { value ->
29
+ body(value)
30
+ }
31
+ }
32
+ }
33
+
34
+ fun buildTypeConverterProvider(): TypeConverterProvider {
35
+ val converterMap = convertersComponent
36
+ .map { it.build() }
37
+ .flatten()
38
+ .toMap()
39
+ return object : TypeConverterProvider {
40
+ override fun obtainTypeConverter(type: KType): TypeConverter<*> {
41
+ val typeConverter = converterMap[type]
42
+ if (typeConverter != null) {
43
+ return typeConverter
44
+ } else {
45
+ throw MissingTypeConverter(type)
46
+ }
47
+ }
48
+ }
49
+ }
50
+ }