expo-modules-core 0.4.9 → 0.6.2

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 (145) hide show
  1. package/CHANGELOG.md +33 -2
  2. package/android/build.gradle +30 -2
  3. package/android/src/main/java/expo/modules/adapters/react/ModuleRegistryAdapter.java +42 -5
  4. package/android/src/main/java/expo/modules/adapters/react/NativeModulesProxy.java +49 -5
  5. package/android/src/main/java/expo/modules/core/BasePackage.java +6 -0
  6. package/android/src/main/java/expo/modules/core/ModulePriorities.kt +25 -0
  7. package/android/src/main/java/expo/modules/core/interfaces/ActivityEventListener.java +3 -1
  8. package/android/src/main/java/expo/modules/core/interfaces/Package.java +4 -0
  9. package/android/src/main/java/expo/modules/core/interfaces/ReactActivityHandler.java +21 -0
  10. package/android/src/main/java/expo/modules/core/interfaces/ReactActivityLifecycleListener.java +23 -0
  11. package/android/src/main/java/expo/modules/core/interfaces/ReactNativeHostHandler.java +14 -0
  12. package/android/src/main/java/expo/modules/core/utilities/KotlinUtilities.kt +23 -0
  13. package/android/src/main/java/expo/modules/kotlin/AppContext.kt +166 -0
  14. package/android/src/main/java/expo/modules/kotlin/DynamicExtenstions.kt +9 -0
  15. package/android/src/main/java/expo/modules/kotlin/ExpoModulesHelper.kt +18 -0
  16. package/android/src/main/java/expo/modules/kotlin/KPromiseWrapper.kt +24 -0
  17. package/android/src/main/java/expo/modules/kotlin/KotlinInteropModuleRegistry.kt +98 -0
  18. package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +41 -0
  19. package/android/src/main/java/expo/modules/kotlin/ModuleRegistry.kt +56 -0
  20. package/android/src/main/java/expo/modules/kotlin/ModulesProvider.kt +7 -0
  21. package/android/src/main/java/expo/modules/kotlin/Promise.kt +13 -0
  22. package/android/src/main/java/expo/modules/kotlin/ReactLifecycleDelegate.kt +39 -0
  23. package/android/src/main/java/expo/modules/kotlin/ReadableArrayIterator.kt +14 -0
  24. package/android/src/main/java/expo/modules/kotlin/ReadableTypeExtensions.kt +18 -0
  25. package/android/src/main/java/expo/modules/kotlin/allocators/ObjectConstructor.kt +5 -0
  26. package/android/src/main/java/expo/modules/kotlin/allocators/ObjectConstructorFactory.kt +31 -0
  27. package/android/src/main/java/expo/modules/kotlin/allocators/UnsafeAllocator.kt +49 -0
  28. package/android/src/main/java/expo/modules/kotlin/events/EventListener.kt +39 -0
  29. package/android/src/main/java/expo/modules/kotlin/events/EventName.kt +31 -0
  30. package/android/src/main/java/expo/modules/kotlin/events/EventsDefinition.kt +3 -0
  31. package/android/src/main/java/expo/modules/kotlin/events/KEventEmitterWrapper.kt +26 -0
  32. package/android/src/main/java/expo/modules/kotlin/events/OnActivityResultPayload.kt +8 -0
  33. package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +70 -0
  34. package/android/src/main/java/expo/modules/kotlin/methods/AnyMethod.kt +50 -0
  35. package/android/src/main/java/expo/modules/kotlin/methods/Method.kt +14 -0
  36. package/android/src/main/java/expo/modules/kotlin/methods/PromiseMethod.kt +15 -0
  37. package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +24 -0
  38. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +227 -0
  39. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionData.kt +16 -0
  40. package/android/src/main/java/expo/modules/kotlin/records/Field.kt +5 -0
  41. package/android/src/main/java/expo/modules/kotlin/records/Record.kt +3 -0
  42. package/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt +55 -0
  43. package/android/src/main/java/expo/modules/kotlin/types/AnyType.kt +14 -0
  44. package/android/src/main/java/expo/modules/kotlin/types/ArrayTypeConverter.kt +44 -0
  45. package/android/src/main/java/expo/modules/kotlin/types/BasicTypeConverters.kt +60 -0
  46. package/android/src/main/java/expo/modules/kotlin/types/EnumTypeConverter.kt +84 -0
  47. package/android/src/main/java/expo/modules/kotlin/types/ListTypeConverter.kt +25 -0
  48. package/android/src/main/java/expo/modules/kotlin/types/MapTypeConverter.kt +39 -0
  49. package/android/src/main/java/expo/modules/kotlin/types/PairTypeConverter.kt +28 -0
  50. package/android/src/main/java/expo/modules/kotlin/types/TypeConverter.kt +19 -0
  51. package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +107 -0
  52. package/android/src/main/java/expo/modules/kotlin/views/AnyViewProp.kt +10 -0
  53. package/android/src/main/java/expo/modules/kotlin/views/ConcreteViewProp.kt +17 -0
  54. package/android/src/main/java/expo/modules/kotlin/views/GroupViewManagerWrapper.kt +22 -0
  55. package/android/src/main/java/expo/modules/kotlin/views/SimpleViewManagerWrapper.kt +21 -0
  56. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinition.kt +41 -0
  57. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinitionBuilder.kt +40 -0
  58. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerWrapperDelegate.kt +21 -0
  59. package/android/src/main/java/expo/modules/kotlin/views/ViewWrapperDelegateHolder.kt +5 -0
  60. package/build/NativeModulesProxy.native.d.ts +4 -0
  61. package/build/NativeModulesProxy.native.js +14 -1
  62. package/build/NativeModulesProxy.native.js.map +1 -1
  63. package/build/NativeModulesProxy.types.d.ts +3 -0
  64. package/build/NativeModulesProxy.types.js.map +1 -1
  65. package/build/NativeViewManagerAdapter.native.js +1 -1
  66. package/build/NativeViewManagerAdapter.native.js.map +1 -1
  67. package/ios/AppDelegates/EXAppDelegateWrapper.h +19 -0
  68. package/ios/AppDelegates/EXAppDelegateWrapper.m +45 -0
  69. package/ios/AppDelegates/EXAppDelegatesLoader.h +15 -0
  70. package/ios/AppDelegates/EXAppDelegatesLoader.m +30 -0
  71. package/ios/AppDelegates/EXLegacyAppDelegateWrapper.h +16 -0
  72. package/ios/{EXAppDelegateWrapper.m → AppDelegates/EXLegacyAppDelegateWrapper.m} +2 -2
  73. package/ios/AppDelegates/ExpoAppDelegate.swift +282 -0
  74. package/ios/AppDelegates/ExpoAppDelegateSubscriber.swift +24 -0
  75. package/ios/EXAppDefines.h +26 -0
  76. package/ios/EXAppDefines.m +61 -0
  77. package/ios/ExpoModulesCore.podspec +8 -3
  78. package/ios/JSI/ExpoModulesProxySpec.h +24 -0
  79. package/ios/JSI/ExpoModulesProxySpec.mm +135 -0
  80. package/ios/JSI/JSIConversions.h +42 -0
  81. package/ios/JSI/JSIConversions.mm +164 -0
  82. package/ios/JSI/JSIInstaller.h +19 -0
  83. package/ios/JSI/JSIInstaller.mm +22 -0
  84. package/ios/ModuleRegistryAdapter/EXModuleRegistryAdapter.m +1 -6
  85. package/ios/NativeModulesProxy/EXNativeModulesProxy.h +6 -0
  86. package/ios/NativeModulesProxy/{EXNativeModulesProxy.m → EXNativeModulesProxy.mm} +63 -17
  87. package/ios/ReactDelegates/EXRCTBridgeDelegateInterceptor.h +16 -0
  88. package/ios/ReactDelegates/EXRCTBridgeDelegateInterceptor.m +49 -0
  89. package/ios/ReactDelegates/EXReactDelegateWrapper+Private.h +18 -0
  90. package/ios/ReactDelegates/EXReactDelegateWrapper.h +25 -0
  91. package/ios/ReactDelegates/EXReactDelegateWrapper.m +40 -0
  92. package/ios/ReactDelegates/ExpoReactDelegate.swift +37 -0
  93. package/ios/ReactDelegates/ExpoReactDelegateHandler.swift +52 -0
  94. package/ios/ReactDelegates/ModulePriorities.swift +20 -0
  95. package/ios/Services/EXReactNativeEventEmitter.h +6 -0
  96. package/ios/Services/EXReactNativeEventEmitter.m +15 -0
  97. package/ios/Swift/AppContext.swift +14 -1
  98. package/ios/Swift/Arguments/AnyArgument.swift +14 -0
  99. package/ios/Swift/Arguments/AnyArgumentType.swift +13 -0
  100. package/ios/Swift/Arguments/ArgumentType.swift +24 -0
  101. package/ios/Swift/Arguments/ConvertibleArgument.swift +15 -0
  102. package/ios/Swift/Arguments/Convertibles.swift +107 -0
  103. package/ios/Swift/Arguments/Types/ArrayArgumentType.swift +42 -0
  104. package/ios/Swift/Arguments/Types/ConvertibleArgumentType.swift +16 -0
  105. package/ios/Swift/Arguments/Types/EnumArgumentType.swift +105 -0
  106. package/ios/Swift/Arguments/Types/OptionalArgumentType.swift +49 -0
  107. package/ios/Swift/Arguments/Types/PromiseArgumentType.swift +15 -0
  108. package/ios/Swift/Arguments/Types/RawArgumentType.swift +25 -0
  109. package/ios/Swift/Conversions.swift +199 -7
  110. package/ios/Swift/EventListener.swift +37 -5
  111. package/ios/Swift/Functions/AnyFunction.swift +42 -0
  112. package/ios/Swift/{Methods/ConcreteMethod.swift → Functions/ConcreteFunction.swift} +32 -34
  113. package/ios/Swift/ModuleHolder.swift +86 -20
  114. package/ios/Swift/ModuleRegistry.swift +19 -8
  115. package/ios/Swift/Modules/AnyModule.swift +8 -8
  116. package/ios/Swift/Modules/Module.swift +11 -0
  117. package/ios/Swift/Modules/ModuleDefinition.swift +55 -15
  118. package/ios/Swift/Modules/ModuleDefinitionBuilder.swift +1 -1
  119. package/ios/Swift/Modules/ModuleDefinitionComponents.swift +149 -54
  120. package/ios/Swift/ModulesProvider.swift +19 -0
  121. package/ios/Swift/Promise.swift +1 -1
  122. package/ios/Swift/Records/Field.swift +1 -1
  123. package/ios/Swift/Records/Record.swift +8 -1
  124. package/ios/Swift/SwiftInteropBridge.swift +46 -17
  125. package/ios/Swift/Views/AnyViewProp.swift +2 -2
  126. package/ios/Swift/Views/ConcreteViewProp.swift +37 -10
  127. package/ios/Swift/Views/ViewModuleWrapper.swift +9 -4
  128. package/ios/Swift.h +9 -0
  129. package/ios/Tests/ArgumentTypeSpec.swift +145 -0
  130. package/ios/Tests/ConstantsSpec.swift +36 -0
  131. package/ios/Tests/ConvertiblesSpec.swift +265 -0
  132. package/ios/Tests/EXAppDefinesTest.m +99 -0
  133. package/ios/Tests/{MethodSpec.swift → FunctionSpec.swift} +69 -54
  134. package/ios/Tests/FunctionWithConvertiblesSpec.swift +66 -0
  135. package/ios/Tests/Mocks/ModuleMocks.swift +21 -7
  136. package/ios/Tests/ModuleEventListenersSpec.swift +17 -16
  137. package/ios/Tests/ModuleRegistrySpec.swift +4 -7
  138. package/package.json +3 -3
  139. package/src/NativeModulesProxy.native.ts +22 -2
  140. package/src/NativeModulesProxy.types.ts +8 -0
  141. package/src/NativeViewManagerAdapter.native.tsx +1 -1
  142. package/ios/EXAppDelegateWrapper.h +0 -13
  143. package/ios/Swift/Methods/AnyArgumentType.swift +0 -48
  144. package/ios/Swift/Methods/AnyMethod.swift +0 -31
  145. package/ios/Swift/Methods/AnyMethodArgument.swift +0 -13
@@ -0,0 +1,15 @@
1
+ package expo.modules.kotlin.methods
2
+
3
+ import expo.modules.kotlin.Promise
4
+ import expo.modules.kotlin.types.AnyType
5
+
6
+ class PromiseMethod(
7
+ name: String,
8
+ argsType: Array<AnyType>,
9
+ private val body: (args: Array<out Any?>, promise: Promise) -> Unit
10
+ ) : AnyMethod(name, argsType) {
11
+
12
+ override fun callImplementation(args: Array<out Any?>, promise: Promise) {
13
+ body(args, promise)
14
+ }
15
+ }
@@ -0,0 +1,24 @@
1
+ package expo.modules.kotlin.modules
2
+
3
+ import android.os.Bundle
4
+ import expo.modules.kotlin.AppContext
5
+
6
+ abstract class Module {
7
+ internal var _appContext: AppContext? = null
8
+
9
+ private val moduleEventEmitter by lazy { appContext.eventEmitter(this) }
10
+
11
+ val appContext: AppContext
12
+ get() = requireNotNull(_appContext) { "The module wasn't created! You can't access the app context." }
13
+
14
+ fun sendEvent(name: String, body: Bundle?) {
15
+ moduleEventEmitter?.emit(name, body)
16
+ }
17
+
18
+ abstract fun definition(): ModuleDefinitionData
19
+ }
20
+
21
+ @Suppress("FunctionName")
22
+ inline fun ModuleDefinition(block: ModuleDefinitionBuilder.() -> Unit): ModuleDefinitionData {
23
+ return ModuleDefinitionBuilder().also(block).build()
24
+ }
@@ -0,0 +1,227 @@
1
+ /**
2
+ * We used a function from the experimental STD API - typeOf (see kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/type-of.html).
3
+ * We shouldn't have any problem with that function, cause it's widely used in other libraries created by JetBrains like kotlinx-serializer.
4
+ * This function is super handy if we want to receive a collection type.
5
+ * For example, it's very hard to obtain the generic parameter type from the list class.
6
+ * In plain Java, it's almost impossible. There is a trick to getting such information using something called TypeToken.
7
+ * For instance, the Gson library uses this workaround. But there still will be a problem with nullability.
8
+ * We didn't find a good solution to distinguish between List<Any?> and List<Any>.
9
+ * Mainly because from the JVM perspective it's the same type.
10
+ * That's why we used typeOf. It solves all problems described above.
11
+ */
12
+ @file:OptIn(ExperimentalStdlibApi::class)
13
+
14
+ package expo.modules.kotlin.modules
15
+
16
+ import android.app.Activity
17
+ import android.content.Intent
18
+ import expo.modules.kotlin.Promise
19
+ import expo.modules.kotlin.events.BasicEventListener
20
+ import expo.modules.kotlin.events.EventListener
21
+ import expo.modules.kotlin.events.EventListenerWithPayload
22
+ import expo.modules.kotlin.events.EventListenerWithSenderAndPayload
23
+ import expo.modules.kotlin.events.EventName
24
+ import expo.modules.kotlin.events.EventsDefinition
25
+ import expo.modules.kotlin.events.OnActivityResultPayload
26
+ import expo.modules.kotlin.methods.AnyMethod
27
+ import expo.modules.kotlin.methods.Method
28
+ import expo.modules.kotlin.methods.PromiseMethod
29
+ import expo.modules.kotlin.types.toAnyType
30
+ import expo.modules.kotlin.views.ViewManagerDefinition
31
+ import expo.modules.kotlin.views.ViewManagerDefinitionBuilder
32
+ import kotlin.reflect.typeOf
33
+
34
+ class ModuleDefinitionBuilder {
35
+ private var name: String? = null
36
+ private var constantsProvider = { emptyMap<String, Any?>() }
37
+ private var eventsDefinition: EventsDefinition? = null
38
+
39
+ @PublishedApi
40
+ internal var methods = mutableMapOf<String, AnyMethod>()
41
+
42
+ @PublishedApi
43
+ internal var viewManagerDefinition: ViewManagerDefinition? = null
44
+
45
+ @PublishedApi
46
+ internal val eventListeners = mutableMapOf<EventName, EventListener>()
47
+
48
+ fun build(): ModuleDefinitionData {
49
+ return ModuleDefinitionData(
50
+ requireNotNull(name),
51
+ constantsProvider,
52
+ methods,
53
+ viewManagerDefinition,
54
+ eventListeners,
55
+ eventsDefinition
56
+ )
57
+ }
58
+
59
+ fun name(name: String) {
60
+ this.name = name
61
+ }
62
+
63
+ fun constants(constantsProvider: () -> Map<String, Any?>) {
64
+ this.constantsProvider = constantsProvider
65
+ }
66
+
67
+ @JvmName("methodWithoutArgs")
68
+ inline fun function(
69
+ name: String,
70
+ crossinline body: () -> Any?
71
+ ) {
72
+ methods[name] = Method(name, arrayOf()) { body() }
73
+ }
74
+
75
+ inline fun <reified R> function(
76
+ name: String,
77
+ crossinline body: () -> R
78
+ ) {
79
+ methods[name] = Method(name, arrayOf()) { body() }
80
+ }
81
+
82
+ inline fun <reified R, reified P0> function(
83
+ name: String,
84
+ crossinline body: (p0: P0) -> R
85
+ ) {
86
+ methods[name] = if (P0::class == Promise::class) {
87
+ PromiseMethod(name, arrayOf()) { _, promise -> body(promise as P0) }
88
+ } else {
89
+ Method(name, arrayOf(typeOf<P0>().toAnyType())) { body(it[0] as P0) }
90
+ }
91
+ }
92
+
93
+ inline fun <reified R, reified P0, reified P1> function(
94
+ name: String,
95
+ crossinline body: (p0: P0, p1: P1) -> R
96
+ ) {
97
+ methods[name] = if (P1::class == Promise::class) {
98
+ PromiseMethod(name, arrayOf(typeOf<P0>().toAnyType())) { args, promise -> body(args[0] as P0, promise as P1) }
99
+ } else {
100
+ Method(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType())) { body(it[0] as P0, it[1] as P1) }
101
+ }
102
+ }
103
+
104
+ inline fun <reified R, reified P0, reified P1, reified P2> function(
105
+ name: String,
106
+ crossinline body: (p0: P0, p1: P1, p2: P2) -> R
107
+ ) {
108
+ methods[name] = if (P2::class == Promise::class) {
109
+ PromiseMethod(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, promise as P2) }
110
+ } else {
111
+ Method(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2) }
112
+ }
113
+ }
114
+
115
+ inline fun <reified R, reified P0, reified P1, reified P2, reified P3> function(
116
+ name: String,
117
+ crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3) -> R
118
+ ) {
119
+ methods[name] = if (P3::class == Promise::class) {
120
+ PromiseMethod(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, promise as P3) }
121
+ } else {
122
+ Method(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3) }
123
+ }
124
+ }
125
+
126
+ inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4> function(
127
+ name: String,
128
+ crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4) -> R
129
+ ) {
130
+ methods[name] = if (P4::class == Promise::class) {
131
+ PromiseMethod(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, args[3] as P3, promise as P4) }
132
+ } else {
133
+ Method(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4) }
134
+ }
135
+ }
136
+
137
+ inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5> function(
138
+ name: String,
139
+ crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) -> R
140
+ ) {
141
+ methods[name] = if (P5::class == Promise::class) {
142
+ PromiseMethod(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, args[3] as P3, args[4] as P4, promise as P5) }
143
+ } else {
144
+ Method(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4, it[5] as P5) }
145
+ }
146
+ }
147
+
148
+ inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5, reified P6> function(
149
+ name: String,
150
+ crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6) -> R
151
+ ) {
152
+ methods[name] = if (P6::class == Promise::class) {
153
+ PromiseMethod(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, args[3] as P3, args[4] as P4, args[5] as P5, promise as P6) }
154
+ } else {
155
+ Method(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType(), typeOf<P6>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4, it[5] as P5, it[6] as P6) }
156
+ }
157
+ }
158
+
159
+ inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5, reified P6, reified P7> function(
160
+ name: String,
161
+ crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7) -> R
162
+ ) {
163
+ methods[name] = if (P7::class == Promise::class) {
164
+ PromiseMethod(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType(), typeOf<P6>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, args[3] as P3, args[4] as P4, args[5] as P5, args[6] as P6, promise as P7) }
165
+ } else {
166
+ Method(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType(), typeOf<P6>().toAnyType(), typeOf<P7>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4, it[5] as P5, it[6] as P6, it[7] as P7) }
167
+ }
168
+ }
169
+
170
+ inline fun viewManager(body: ViewManagerDefinitionBuilder.() -> Unit) {
171
+ require(viewManagerDefinition == null) { "The module definition may have exported only one view manager." }
172
+
173
+ val viewManagerDefinitionBuilder = ViewManagerDefinitionBuilder()
174
+ body.invoke(viewManagerDefinitionBuilder)
175
+ viewManagerDefinition = viewManagerDefinitionBuilder.build()
176
+ }
177
+
178
+ inline fun onCreate(crossinline body: () -> Unit) {
179
+ eventListeners[EventName.MODULE_CREATE] = BasicEventListener(EventName.MODULE_CREATE) { body() }
180
+ }
181
+
182
+ inline fun onDestroy(crossinline body: () -> Unit) {
183
+ eventListeners[EventName.MODULE_DESTROY] = BasicEventListener(EventName.MODULE_DESTROY) { body() }
184
+ }
185
+
186
+ inline fun onActivityEntersForeground(crossinline body: () -> Unit) {
187
+ eventListeners[EventName.ACTIVITY_ENTERS_FOREGROUND] = BasicEventListener(EventName.ACTIVITY_ENTERS_FOREGROUND) { body() }
188
+ }
189
+
190
+ inline fun onActivityEntersBackground(crossinline body: () -> Unit) {
191
+ eventListeners[EventName.ACTIVITY_ENTERS_BACKGROUND] = BasicEventListener(EventName.ACTIVITY_ENTERS_BACKGROUND) { body() }
192
+ }
193
+
194
+ inline fun onActivityDestroys(crossinline body: () -> Unit) {
195
+ eventListeners[EventName.ACTIVITY_DESTROYS] = BasicEventListener(EventName.ACTIVITY_DESTROYS) { body() }
196
+ }
197
+
198
+ /**
199
+ * Defines event names that this module can send to JavaScript.
200
+ */
201
+ fun events(vararg events: String) {
202
+ eventsDefinition = EventsDefinition(events)
203
+ }
204
+
205
+ /**
206
+ * Method that is invoked when the first event listener is added.
207
+ */
208
+ inline fun onStartObserving(crossinline body: () -> Unit) {
209
+ function("startObserving", body)
210
+ }
211
+
212
+ /**
213
+ * Method that is invoked when all event listeners are removed.
214
+ */
215
+ inline fun onStopObserving(crossinline body: () -> Unit) {
216
+ function("stopObserving", body)
217
+ }
218
+
219
+ inline fun onNewIntent(crossinline body: (Intent) -> Unit) {
220
+ eventListeners[EventName.ON_NEW_INTENT] = EventListenerWithPayload<Intent>(EventName.ON_NEW_INTENT) { body(it) }
221
+ }
222
+
223
+ inline fun onActivityResult(crossinline body: (Activity, OnActivityResultPayload) -> Unit) {
224
+ eventListeners[EventName.ON_ACTIVITY_RESULT] =
225
+ EventListenerWithSenderAndPayload<Activity, OnActivityResultPayload>(EventName.ON_ACTIVITY_RESULT) { sender, payload -> body(sender, payload) }
226
+ }
227
+ }
@@ -0,0 +1,16 @@
1
+ package expo.modules.kotlin.modules
2
+
3
+ import expo.modules.kotlin.events.EventListener
4
+ import expo.modules.kotlin.events.EventName
5
+ import expo.modules.kotlin.events.EventsDefinition
6
+ import expo.modules.kotlin.methods.AnyMethod
7
+ import expo.modules.kotlin.views.ViewManagerDefinition
8
+
9
+ class ModuleDefinitionData(
10
+ val name: String,
11
+ val constantsProvider: () -> Map<String, Any?>,
12
+ val methods: Map<String, AnyMethod>,
13
+ val viewManagerDefinition: ViewManagerDefinition? = null,
14
+ val eventListeners: Map<EventName, EventListener> = emptyMap(),
15
+ val eventsDefinition: EventsDefinition? = null
16
+ )
@@ -0,0 +1,5 @@
1
+ package expo.modules.kotlin.records
2
+
3
+ @Target(AnnotationTarget.PROPERTY)
4
+ @Retention(AnnotationRetention.RUNTIME)
5
+ annotation class Field(val key: String = "")
@@ -0,0 +1,3 @@
1
+ package expo.modules.kotlin.records
2
+
3
+ interface Record
@@ -0,0 +1,55 @@
1
+ package expo.modules.kotlin.records
2
+
3
+ import com.facebook.react.bridge.Dynamic
4
+ import expo.modules.kotlin.allocators.ObjectConstructor
5
+ import expo.modules.kotlin.allocators.ObjectConstructorFactory
6
+ import expo.modules.kotlin.types.TypeConverter
7
+ import expo.modules.kotlin.types.TypeConverterProvider
8
+ import kotlin.reflect.KClass
9
+ import kotlin.reflect.KType
10
+ import kotlin.reflect.full.findAnnotation
11
+ import kotlin.reflect.full.memberProperties
12
+ import kotlin.reflect.jvm.javaField
13
+
14
+ // TODO(@lukmccall): create all converters during initialization
15
+ class RecordTypeConverter<T : Record>(
16
+ private val converterProvider: TypeConverterProvider,
17
+ val type: KType,
18
+ ) : TypeConverter<T>(type.isMarkedNullable) {
19
+ private val objectConstructorFactory = ObjectConstructorFactory()
20
+
21
+ override fun convertNonOptional(value: Dynamic): T {
22
+ val jsMap = value.asMap()
23
+
24
+ val kClass = type.classifier as KClass<*>
25
+ val instance = getObjectConstructor(kClass.java).construct()
26
+
27
+ kClass
28
+ .memberProperties
29
+ .map { property ->
30
+ val filedInformation = property.findAnnotation<Field>() ?: return@map
31
+ val jsKey = filedInformation.key.takeUnless { it == "" } ?: property.name
32
+
33
+ if (!jsMap.hasKey(jsKey)) {
34
+ // TODO(@lukmccall): handle required keys
35
+ return@map
36
+ }
37
+
38
+ val value = jsMap.getDynamic(jsKey)
39
+ val javaField = property.javaField!!
40
+
41
+ val elementConverter = converterProvider.obtainTypeConverter(property.returnType)
42
+ val casted = elementConverter.convert(value)
43
+
44
+ javaField.isAccessible = true
45
+ javaField.set(instance, casted)
46
+ }
47
+
48
+ @Suppress("UNCHECKED_CAST")
49
+ return instance as T
50
+ }
51
+
52
+ private fun <T> getObjectConstructor(clazz: Class<T>): ObjectConstructor<T> {
53
+ return objectConstructorFactory.get(clazz)
54
+ }
55
+ }
@@ -0,0 +1,14 @@
1
+ package expo.modules.kotlin.types
2
+
3
+ import com.facebook.react.bridge.Dynamic
4
+ import kotlin.reflect.KType
5
+
6
+ fun KType.toAnyType(): AnyType = AnyType(this)
7
+
8
+ class AnyType(val kType: KType) {
9
+ private val converter: TypeConverter<*> by lazy {
10
+ TypeConverterProviderImpl.obtainTypeConverter(kType)
11
+ }
12
+
13
+ fun convert(value: Dynamic): Any? = converter.convert(value)
14
+ }
@@ -0,0 +1,44 @@
1
+ package expo.modules.kotlin.types
2
+
3
+ import com.facebook.react.bridge.Dynamic
4
+ import expo.modules.kotlin.recycle
5
+ import kotlin.reflect.KClass
6
+ import kotlin.reflect.KType
7
+
8
+ class ArrayTypeConverter(
9
+ converterProvider: TypeConverterProvider,
10
+ private val type: KType,
11
+ ) : TypeConverter<Array<*>>(type.isMarkedNullable) {
12
+ private val arrayElementConverter = converterProvider.obtainTypeConverter(
13
+ requireNotNull(type.arguments.first().type) {
14
+ "The array type should contain the type of the elements."
15
+ }
16
+ )
17
+
18
+ override fun convertNonOptional(value: Dynamic): Array<*> {
19
+ val jsArray = value.asArray()
20
+ val array = createTypedArray(jsArray.size())
21
+ for (i in 0 until jsArray.size()) {
22
+ array[i] = jsArray
23
+ .getDynamic(i)
24
+ .recycle {
25
+ arrayElementConverter.convert(this)
26
+ }
27
+ }
28
+ return array
29
+ }
30
+
31
+ /**
32
+ * We can't use a Array<Any?> here. We have to create a typed array.
33
+ * Otherwise, cast which is done before calling lambda provided by the user will always fail.
34
+ * For JVM, Array<String> is a different type than Array<Any?>.
35
+ * The first one is translated to `[Ljava.lang.String;` but the second one is translated to `[java.lang.Object;`.
36
+ */
37
+ @Suppress("UNCHECKED_CAST")
38
+ private fun createTypedArray(size: Int): Array<Any?> {
39
+ return java.lang.reflect.Array.newInstance(
40
+ (type.arguments.first().type!!.classifier as KClass<*>).java,
41
+ size
42
+ ) as Array<Any?>
43
+ }
44
+ }
@@ -0,0 +1,60 @@
1
+ package expo.modules.kotlin.types
2
+
3
+ import com.facebook.react.bridge.Dynamic
4
+ import com.facebook.react.bridge.ReadableArray
5
+ import com.facebook.react.bridge.ReadableMap
6
+
7
+ class IntTypeConverter(isOptional: Boolean) : TypeConverter<Int>(isOptional) {
8
+ override fun convertNonOptional(value: Dynamic): Int = value.asInt()
9
+ }
10
+
11
+ class DoubleTypeConverter(isOptional: Boolean) : TypeConverter<Double>(isOptional) {
12
+ override fun convertNonOptional(value: Dynamic): Double = value.asDouble()
13
+ }
14
+
15
+ class FloatTypeConverter(isOptional: Boolean) : TypeConverter<Float>(isOptional) {
16
+ override fun convertNonOptional(value: Dynamic): Float = value.asDouble().toFloat()
17
+ }
18
+
19
+ class BoolTypeConverter(isOptional: Boolean) : TypeConverter<Boolean>(isOptional) {
20
+ override fun convertNonOptional(value: Dynamic): Boolean = value.asBoolean()
21
+ }
22
+
23
+ class StringTypeConverter(isOptional: Boolean) : TypeConverter<String>(isOptional) {
24
+ override fun convertNonOptional(value: Dynamic): String = value.asString()
25
+ }
26
+
27
+ class ReadableArrayTypeConverter(isOptional: Boolean) : TypeConverter<ReadableArray>(isOptional) {
28
+ override fun convertNonOptional(value: Dynamic): ReadableArray = value.asArray()
29
+ }
30
+
31
+ class ReadableMapTypeConverter(isOptional: Boolean) : TypeConverter<ReadableMap>(isOptional) {
32
+ override fun convertNonOptional(value: Dynamic): ReadableMap = value.asMap()
33
+ }
34
+
35
+ class PrimitiveIntArrayTypeConverter(isOptional: Boolean) : TypeConverter<IntArray>(isOptional) {
36
+ override fun convertNonOptional(value: Dynamic): IntArray {
37
+ val jsArray = value.asArray()
38
+ return IntArray(jsArray.size()) { index ->
39
+ jsArray.getInt(index)
40
+ }
41
+ }
42
+ }
43
+
44
+ class PrimitiveDoubleArrayTypeConverter(isOptional: Boolean) : TypeConverter<DoubleArray>(isOptional) {
45
+ override fun convertNonOptional(value: Dynamic): DoubleArray {
46
+ val jsArray = value.asArray()
47
+ return DoubleArray(jsArray.size()) { index ->
48
+ jsArray.getDouble(index)
49
+ }
50
+ }
51
+ }
52
+
53
+ class PrimitiveFloatArrayTypeConverter(isOptional: Boolean) : TypeConverter<FloatArray>(isOptional) {
54
+ override fun convertNonOptional(value: Dynamic): FloatArray {
55
+ val jsArray = value.asArray()
56
+ return FloatArray(jsArray.size()) { index ->
57
+ jsArray.getDouble(index).toFloat()
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,84 @@
1
+ package expo.modules.kotlin.types
2
+
3
+ import com.facebook.react.bridge.Dynamic
4
+ import expo.modules.kotlin.exception.IncompatibleArgTypeException
5
+ import expo.modules.kotlin.toKType
6
+ import kotlin.reflect.KClass
7
+ import kotlin.reflect.full.createType
8
+ import kotlin.reflect.full.declaredMemberProperties
9
+ import kotlin.reflect.full.primaryConstructor
10
+
11
+ class EnumTypeConverter(
12
+ private val enumClass: KClass<Enum<*>>,
13
+ isOptional: Boolean
14
+ ) : TypeConverter<Enum<*>>(isOptional) {
15
+ override fun convertNonOptional(value: Dynamic): Enum<*> {
16
+ @Suppress("UNCHECKED_CAST")
17
+ val enumConstants = requireNotNull(enumClass.java.enumConstants) {
18
+ "Passed type is not an enum type."
19
+ }
20
+ require(enumConstants.isNotEmpty()) {
21
+ "Passed enum type is empty."
22
+ }
23
+
24
+ val primaryConstructor = requireNotNull(enumClass.primaryConstructor) {
25
+ "Cannot convert js value to enum without the primary constructor."
26
+ }
27
+
28
+ if (primaryConstructor.parameters.isEmpty()) {
29
+ return convertEnumWithoutParameter(value, enumConstants)
30
+ } else if (primaryConstructor.parameters.size == 1) {
31
+ return convertEnumWithParameter(
32
+ value,
33
+ enumConstants,
34
+ primaryConstructor.parameters.first().name!!
35
+ )
36
+ }
37
+
38
+ throw IncompatibleArgTypeException(value.type.toKType(), enumClass.createType())
39
+ }
40
+
41
+ /**
42
+ * If the primary constructor doesn't take any parameters, we treat the name of each enum as a value.
43
+ * So the jsValue has to contain string.
44
+ */
45
+ private fun convertEnumWithoutParameter(
46
+ jsValue: Dynamic,
47
+ enumConstants: Array<out Enum<*>>
48
+ ): Enum<*> {
49
+ val unwrappedJsValue = jsValue.asString()
50
+ return requireNotNull(
51
+ enumConstants.find { it.name == unwrappedJsValue }
52
+ ) { "Couldn't convert ${jsValue.asString()} to ${enumClass.simpleName}." }
53
+ }
54
+
55
+ /**
56
+ * If the primary constructor take one parameter, we treat this parameter as a enum value.
57
+ * In that case, we handles two different types: Int and String.
58
+ */
59
+ private fun convertEnumWithParameter(
60
+ jsValue: Dynamic,
61
+ enumConstants: Array<out Enum<*>>,
62
+ parameterName: String
63
+ ): Enum<*> {
64
+ // To obtain the value of parameter, we have to find a property that is connected with this parameter.
65
+ @Suppress("UNCHECKED_CAST")
66
+ val parameterProperty = enumClass
67
+ .declaredMemberProperties
68
+ .find { it.name == parameterName }
69
+ requireNotNull(parameterProperty) { "Cannot find a property for $parameterName parameter." }
70
+
71
+ val parameterType = parameterProperty.returnType.classifier
72
+ val jsUnwrapValue = if (parameterType == String::class) {
73
+ jsValue.asString()
74
+ } else {
75
+ jsValue.asInt()
76
+ }
77
+
78
+ return requireNotNull(
79
+ enumConstants.find {
80
+ parameterProperty.get(it) == jsUnwrapValue
81
+ }
82
+ ) { "Couldn't convert ${jsValue.asString()} to ${enumClass.simpleName} where $parameterName is the enum parameter. " }
83
+ }
84
+ }
@@ -0,0 +1,25 @@
1
+ package expo.modules.kotlin.types
2
+
3
+ import com.facebook.react.bridge.Dynamic
4
+ import expo.modules.kotlin.recycle
5
+ import kotlin.reflect.KType
6
+
7
+ class ListTypeConverter(
8
+ converterProvider: TypeConverterProvider,
9
+ type: KType,
10
+ ) : TypeConverter<List<*>>(type.isMarkedNullable) {
11
+ private val elementConverter = converterProvider.obtainTypeConverter(
12
+ requireNotNull(type.arguments.first().type) {
13
+ "The list type should contain the type of elements."
14
+ }
15
+ )
16
+
17
+ override fun convertNonOptional(value: Dynamic): List<*> {
18
+ val jsArray = value.asArray()
19
+ return List(jsArray.size()) { index ->
20
+ jsArray.getDynamic(index).recycle {
21
+ elementConverter.convert(this)
22
+ }
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,39 @@
1
+ @file:OptIn(ExperimentalStdlibApi::class)
2
+
3
+ package expo.modules.kotlin.types
4
+
5
+ import com.facebook.react.bridge.Dynamic
6
+ import com.facebook.react.bridge.DynamicFromObject
7
+ import expo.modules.kotlin.recycle
8
+ import kotlin.reflect.KType
9
+ import kotlin.reflect.typeOf
10
+
11
+ class MapTypeConverter(
12
+ converterProvider: TypeConverterProvider,
13
+ type: KType
14
+ ) : TypeConverter<Map<*, *>>(type.isMarkedNullable) {
15
+ init {
16
+ require(type.arguments.first().type == typeOf<String>()) {
17
+ "The map key type should be String, but received ${type.arguments.first()}."
18
+ }
19
+ }
20
+
21
+ private val valueConverter = converterProvider.obtainTypeConverter(
22
+ requireNotNull(type.arguments.getOrNull(1)?.type) {
23
+ "The map type should contain the key type."
24
+ }
25
+ )
26
+
27
+ override fun convertNonOptional(value: Dynamic): Map<*, *> {
28
+ val jsMap = value.asMap()
29
+ val result = mutableMapOf<String, Any?>()
30
+
31
+ jsMap.entryIterator.forEach { (key, value) ->
32
+ DynamicFromObject(value).recycle {
33
+ result[key] = valueConverter.convert(this)
34
+ }
35
+ }
36
+
37
+ return result
38
+ }
39
+ }
@@ -0,0 +1,28 @@
1
+ package expo.modules.kotlin.types
2
+
3
+ import com.facebook.react.bridge.Dynamic
4
+ import kotlin.reflect.KType
5
+
6
+ class PairTypeConverter(
7
+ converterProvider: TypeConverterProvider,
8
+ type: KType,
9
+ ) : TypeConverter<Pair<*, *>>(type.isMarkedNullable) {
10
+ private val firstConverter = converterProvider.obtainTypeConverter(
11
+ requireNotNull(type.arguments.getOrNull(0)?.type) {
12
+ "The pair type should contain the type of the first parameter."
13
+ }
14
+ )
15
+ private val secondConverter = converterProvider.obtainTypeConverter(
16
+ requireNotNull(type.arguments.getOrNull(1)?.type) {
17
+ "The pair type should contain the type of the second parameter."
18
+ }
19
+ )
20
+
21
+ override fun convertNonOptional(value: Dynamic): Pair<*, *> {
22
+ val jsArray = value.asArray()
23
+ return Pair(
24
+ firstConverter.convert(jsArray.getDynamic(0)),
25
+ secondConverter.convert(jsArray.getDynamic(1)),
26
+ )
27
+ }
28
+ }
@@ -0,0 +1,19 @@
1
+ package expo.modules.kotlin.types
2
+
3
+ import com.facebook.react.bridge.Dynamic
4
+
5
+ abstract class TypeConverter<Type : Any>(
6
+ private val isOptional: Boolean
7
+ ) {
8
+ open fun convert(value: Dynamic): Type? {
9
+ if (value.isNull) {
10
+ if (isOptional) {
11
+ return null
12
+ }
13
+ throw IllegalArgumentException()
14
+ }
15
+ return convertNonOptional(value)
16
+ }
17
+
18
+ abstract fun convertNonOptional(value: Dynamic): Type
19
+ }