expo-modules-core 0.4.6 → 0.6.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 (150) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +6 -6
  3. package/android/build.gradle +30 -2
  4. package/android/src/main/java/expo/modules/adapters/react/ModuleRegistryAdapter.java +27 -5
  5. package/android/src/main/java/expo/modules/adapters/react/NativeModulesProxy.java +49 -5
  6. package/android/src/main/java/expo/modules/core/BasePackage.java +6 -0
  7. package/android/src/main/java/expo/modules/core/ModulePriorities.kt +25 -0
  8. package/android/src/main/java/expo/modules/core/interfaces/ActivityEventListener.java +3 -1
  9. package/android/src/main/java/expo/modules/core/interfaces/ApplicationLifecycleListener.java +10 -0
  10. package/android/src/main/java/expo/modules/core/interfaces/Package.java +4 -0
  11. package/android/src/main/java/expo/modules/core/interfaces/ReactActivityHandler.java +21 -0
  12. package/android/src/main/java/expo/modules/core/interfaces/ReactActivityLifecycleListener.java +14 -0
  13. package/android/src/main/java/expo/modules/core/interfaces/ReactNativeHostHandler.java +70 -0
  14. package/android/src/main/java/expo/modules/core/utilities/KotlinUtilities.kt +23 -0
  15. package/android/src/main/java/expo/modules/kotlin/AppContext.kt +166 -0
  16. package/android/src/main/java/expo/modules/kotlin/DynamicExtenstions.kt +9 -0
  17. package/android/src/main/java/expo/modules/kotlin/ExpoModulesHelper.kt +18 -0
  18. package/android/src/main/java/expo/modules/kotlin/KPromiseWrapper.kt +24 -0
  19. package/android/src/main/java/expo/modules/kotlin/KotlinInteropModuleRegistry.kt +98 -0
  20. package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +41 -0
  21. package/android/src/main/java/expo/modules/kotlin/ModuleRegistry.kt +56 -0
  22. package/android/src/main/java/expo/modules/kotlin/ModulesProvider.kt +7 -0
  23. package/android/src/main/java/expo/modules/kotlin/Promise.kt +13 -0
  24. package/android/src/main/java/expo/modules/kotlin/ReactLifecycleDelegate.kt +39 -0
  25. package/android/src/main/java/expo/modules/kotlin/ReadableArrayIterator.kt +14 -0
  26. package/android/src/main/java/expo/modules/kotlin/ReadableTypeExtensions.kt +18 -0
  27. package/android/src/main/java/expo/modules/kotlin/allocators/ObjectConstructor.kt +5 -0
  28. package/android/src/main/java/expo/modules/kotlin/allocators/ObjectConstructorFactory.kt +31 -0
  29. package/android/src/main/java/expo/modules/kotlin/allocators/UnsafeAllocator.kt +49 -0
  30. package/android/src/main/java/expo/modules/kotlin/events/EventListener.kt +39 -0
  31. package/android/src/main/java/expo/modules/kotlin/events/EventName.kt +31 -0
  32. package/android/src/main/java/expo/modules/kotlin/events/EventsDefinition.kt +3 -0
  33. package/android/src/main/java/expo/modules/kotlin/events/KEventEmitterWrapper.kt +26 -0
  34. package/android/src/main/java/expo/modules/kotlin/events/OnActivityResultPayload.kt +8 -0
  35. package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +70 -0
  36. package/android/src/main/java/expo/modules/kotlin/methods/AnyMethod.kt +50 -0
  37. package/android/src/main/java/expo/modules/kotlin/methods/Method.kt +14 -0
  38. package/android/src/main/java/expo/modules/kotlin/methods/PromiseMethod.kt +15 -0
  39. package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +24 -0
  40. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +227 -0
  41. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionData.kt +16 -0
  42. package/android/src/main/java/expo/modules/kotlin/records/Field.kt +5 -0
  43. package/android/src/main/java/expo/modules/kotlin/records/Record.kt +3 -0
  44. package/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt +55 -0
  45. package/android/src/main/java/expo/modules/kotlin/types/AnyType.kt +14 -0
  46. package/android/src/main/java/expo/modules/kotlin/types/ArrayTypeConverter.kt +44 -0
  47. package/android/src/main/java/expo/modules/kotlin/types/BasicTypeConverters.kt +60 -0
  48. package/android/src/main/java/expo/modules/kotlin/types/EnumTypeConverter.kt +84 -0
  49. package/android/src/main/java/expo/modules/kotlin/types/ListTypeConverter.kt +25 -0
  50. package/android/src/main/java/expo/modules/kotlin/types/MapTypeConverter.kt +39 -0
  51. package/android/src/main/java/expo/modules/kotlin/types/PairTypeConverter.kt +28 -0
  52. package/android/src/main/java/expo/modules/kotlin/types/TypeConverter.kt +19 -0
  53. package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +107 -0
  54. package/android/src/main/java/expo/modules/kotlin/views/AnyViewProp.kt +10 -0
  55. package/android/src/main/java/expo/modules/kotlin/views/ConcreteViewProp.kt +17 -0
  56. package/android/src/main/java/expo/modules/kotlin/views/GroupViewManagerWrapper.kt +22 -0
  57. package/android/src/main/java/expo/modules/kotlin/views/SimpleViewManagerWrapper.kt +21 -0
  58. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinition.kt +41 -0
  59. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinitionBuilder.kt +40 -0
  60. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerWrapperDelegate.kt +21 -0
  61. package/android/src/main/java/expo/modules/kotlin/views/ViewWrapperDelegateHolder.kt +5 -0
  62. package/build/NativeModulesProxy.native.d.ts +4 -0
  63. package/build/NativeModulesProxy.native.js +14 -1
  64. package/build/NativeModulesProxy.native.js.map +1 -1
  65. package/build/NativeModulesProxy.types.d.ts +3 -0
  66. package/build/NativeModulesProxy.types.js.map +1 -1
  67. package/build/NativeViewManagerAdapter.native.js +1 -1
  68. package/build/NativeViewManagerAdapter.native.js.map +1 -1
  69. package/ios/AppDelegates/EXAppDelegateWrapper.h +19 -0
  70. package/ios/AppDelegates/EXAppDelegateWrapper.m +45 -0
  71. package/ios/AppDelegates/EXAppDelegatesLoader.h +15 -0
  72. package/ios/AppDelegates/EXAppDelegatesLoader.m +30 -0
  73. package/ios/AppDelegates/EXLegacyAppDelegateWrapper.h +16 -0
  74. package/ios/{EXAppDelegateWrapper.m → AppDelegates/EXLegacyAppDelegateWrapper.m} +2 -2
  75. package/ios/AppDelegates/ExpoAppDelegate.swift +282 -0
  76. package/ios/AppDelegates/ExpoAppDelegateSubscriber.swift +24 -0
  77. package/ios/EXAppDefines.h +26 -0
  78. package/ios/EXAppDefines.m +61 -0
  79. package/ios/ExpoModulesCore.podspec +8 -3
  80. package/ios/JSI/ExpoModulesProxySpec.h +24 -0
  81. package/ios/JSI/ExpoModulesProxySpec.mm +135 -0
  82. package/ios/JSI/JSIConversions.h +42 -0
  83. package/ios/JSI/JSIConversions.mm +164 -0
  84. package/ios/JSI/JSIInstaller.h +19 -0
  85. package/ios/JSI/JSIInstaller.mm +22 -0
  86. package/ios/ModuleRegistryAdapter/EXModuleRegistryAdapter.m +1 -6
  87. package/ios/NativeModulesProxy/EXNativeModulesProxy.h +6 -0
  88. package/ios/NativeModulesProxy/{EXNativeModulesProxy.m → EXNativeModulesProxy.mm} +91 -18
  89. package/ios/ReactDelegates/EXRCTBridgeDelegateInterceptor.h +16 -0
  90. package/ios/ReactDelegates/EXRCTBridgeDelegateInterceptor.m +49 -0
  91. package/ios/ReactDelegates/EXReactDelegateWrapper+Private.h +18 -0
  92. package/ios/ReactDelegates/EXReactDelegateWrapper.h +25 -0
  93. package/ios/ReactDelegates/EXReactDelegateWrapper.m +40 -0
  94. package/ios/ReactDelegates/ExpoReactDelegate.swift +37 -0
  95. package/ios/ReactDelegates/ExpoReactDelegateHandler.swift +52 -0
  96. package/ios/ReactDelegates/ModulePriorities.swift +20 -0
  97. package/ios/Services/EXReactNativeEventEmitter.h +6 -0
  98. package/ios/Services/EXReactNativeEventEmitter.m +15 -0
  99. package/ios/Swift/AppContext.swift +14 -1
  100. package/ios/Swift/Arguments/AnyArgument.swift +14 -0
  101. package/ios/Swift/Arguments/AnyArgumentType.swift +13 -0
  102. package/ios/Swift/Arguments/ArgumentType.swift +24 -0
  103. package/ios/Swift/Arguments/ConvertibleArgument.swift +15 -0
  104. package/ios/Swift/Arguments/Convertibles.swift +107 -0
  105. package/ios/Swift/Arguments/Types/ArrayArgumentType.swift +42 -0
  106. package/ios/Swift/Arguments/Types/ConvertibleArgumentType.swift +16 -0
  107. package/ios/Swift/Arguments/Types/EnumArgumentType.swift +105 -0
  108. package/ios/Swift/Arguments/Types/OptionalArgumentType.swift +49 -0
  109. package/ios/Swift/Arguments/Types/PromiseArgumentType.swift +15 -0
  110. package/ios/Swift/Arguments/Types/RawArgumentType.swift +25 -0
  111. package/ios/Swift/Conversions.swift +199 -7
  112. package/ios/Swift/EventListener.swift +37 -5
  113. package/ios/Swift/Functions/AnyFunction.swift +42 -0
  114. package/ios/Swift/{Methods/ConcreteMethod.swift → Functions/ConcreteFunction.swift} +32 -34
  115. package/ios/Swift/ModuleHolder.swift +86 -20
  116. package/ios/Swift/ModuleRegistry.swift +19 -8
  117. package/ios/Swift/Modules/AnyModule.swift +8 -8
  118. package/ios/Swift/Modules/Module.swift +11 -0
  119. package/ios/Swift/Modules/ModuleDefinition.swift +55 -15
  120. package/ios/Swift/Modules/ModuleDefinitionBuilder.swift +1 -1
  121. package/ios/Swift/Modules/ModuleDefinitionComponents.swift +149 -54
  122. package/ios/Swift/ModulesProvider.swift +19 -0
  123. package/ios/Swift/Promise.swift +1 -1
  124. package/ios/Swift/Records/Field.swift +1 -1
  125. package/ios/Swift/Records/Record.swift +8 -1
  126. package/ios/Swift/SwiftInteropBridge.swift +46 -17
  127. package/ios/Swift/Views/AnyViewProp.swift +2 -2
  128. package/ios/Swift/Views/ConcreteViewProp.swift +37 -10
  129. package/ios/Swift/Views/ViewModuleWrapper.swift +9 -4
  130. package/ios/Swift.h +9 -0
  131. package/ios/Tests/ArgumentTypeSpec.swift +145 -0
  132. package/ios/Tests/ConstantsSpec.swift +36 -0
  133. package/ios/Tests/ConvertiblesSpec.swift +265 -0
  134. package/ios/Tests/EXAppDefinesTest.m +99 -0
  135. package/ios/Tests/{MethodSpec.swift → FunctionSpec.swift} +69 -54
  136. package/ios/Tests/FunctionWithConvertiblesSpec.swift +66 -0
  137. package/ios/Tests/Mocks/ModuleMocks.swift +21 -7
  138. package/ios/Tests/ModuleEventListenersSpec.swift +17 -16
  139. package/ios/Tests/ModuleRegistrySpec.swift +4 -7
  140. package/package.json +3 -3
  141. package/src/NativeModulesProxy.native.ts +22 -2
  142. package/src/NativeModulesProxy.types.ts +8 -0
  143. package/src/NativeViewManagerAdapter.native.tsx +1 -1
  144. package/android/src/main/java/expo/modules/core/interfaces/ApplicationLifecycleListener.kt +0 -9
  145. package/android/src/main/java/expo/modules/core/interfaces/ReactActivityLifecycleListener.kt +0 -11
  146. package/android/src/main/java/expo/modules/core/interfaces/ReactNativeHostHandler.kt +0 -51
  147. package/ios/EXAppDelegateWrapper.h +0 -13
  148. package/ios/Swift/Methods/AnyArgumentType.swift +0 -48
  149. package/ios/Swift/Methods/AnyMethod.swift +0 -31
  150. package/ios/Swift/Methods/AnyMethodArgument.swift +0 -13
@@ -0,0 +1,26 @@
1
+ package expo.modules.kotlin.events
2
+
3
+ import android.os.Bundle
4
+ import expo.modules.core.interfaces.services.EventEmitter
5
+ import expo.modules.kotlin.ModuleHolder
6
+
7
+ /**
8
+ * In Swift, we check if the event is supported. Otherwise, we can't send such event through the bridge.
9
+ * However, in Kotlin this is unnecessary, but to make our API consistent, we added similar check.
10
+ * But because of that, we had to create a wrapper for EventEmitter.
11
+ */
12
+ class KEventEmitterWrapper(
13
+ private val moduleHolder: ModuleHolder,
14
+ private val legacyEventEmitter: EventEmitter
15
+ ) : EventEmitter by legacyEventEmitter {
16
+ override fun emit(eventName: String, eventBody: Bundle?) {
17
+ require(
18
+ moduleHolder
19
+ .definition
20
+ .eventsDefinition
21
+ ?.names
22
+ ?.contains(eventName) == true
23
+ ) { "Unsupported event: $eventName." }
24
+ legacyEventEmitter.emit(eventName, eventBody)
25
+ }
26
+ }
@@ -0,0 +1,8 @@
1
+ package expo.modules.kotlin.events
2
+
3
+ import android.content.Intent
4
+
5
+ /**
6
+ * Payload for the `onActivityResult` event.
7
+ */
8
+ data class OnActivityResultPayload(val requestCode: Int, val resultCode: Int, val data: Intent?)
@@ -0,0 +1,70 @@
1
+ package expo.modules.kotlin.exception
2
+
3
+ import java.util.*
4
+ import kotlin.reflect.KType
5
+
6
+ /**
7
+ * A class for errors specifying its `code` and providing the `description`.
8
+ */
9
+ open class CodedException(
10
+ message: String?,
11
+ cause: Throwable?
12
+ ) : Exception(message, cause) {
13
+ // We need that secondary property, cause we can't access
14
+ // the javaClass property in the constructor.
15
+ private var providedCode: String? = null
16
+
17
+ val code
18
+ get() = providedCode ?: inferCode(javaClass)
19
+
20
+ constructor(code: String, message: String, cause: Throwable?) : this(message, cause) {
21
+ providedCode = code
22
+ }
23
+
24
+ constructor(message: String) : this(message, null)
25
+
26
+ constructor(cause: Throwable) : this(cause.localizedMessage, cause)
27
+
28
+ constructor() : this(null, null)
29
+
30
+ companion object {
31
+ /**
32
+ * The code is inferred from the class name — e.g. the code of `ModuleNotFoundException` becomes `ERR_MODULE_NOT_FOUND`.
33
+ */
34
+ private fun inferCode(clazz: Class<*>): String {
35
+ val name = requireNotNull(clazz.simpleName) { "Cannot infer code name from class name. We don't support anonymous classes." }
36
+
37
+ return "ERR_" + name
38
+ .replace("(Exception)$".toRegex(), "")
39
+ .replace("(.)([A-Z])".toRegex(), "$1_$2")
40
+ .toUpperCase(Locale.ROOT)
41
+ }
42
+ }
43
+ }
44
+
45
+ internal class IncompatibleArgTypeException(
46
+ argumentType: KType,
47
+ desiredType: KType,
48
+ cause: Throwable? = null
49
+ ) : CodedException(
50
+ message = "Argument type $argumentType is not compatible with expected type $desiredType.",
51
+ cause = cause
52
+ )
53
+
54
+ internal class MissingTypeConverter(
55
+ forType: KType
56
+ ) : CodedException(
57
+ message = "Cannot find type converter for $forType.",
58
+ )
59
+
60
+ internal class InvalidArgsNumberException(received: Int, expected: Int) :
61
+ CodedException(message = "Received $received arguments, but $expected was expected.")
62
+
63
+ internal class MethodNotFoundException(methodName: String, moduleName: String) :
64
+ CodedException(message = "Cannot fund method $methodName in module $moduleName")
65
+
66
+ internal class NullArgumentException(desiredType: KType) :
67
+ CodedException(message = "Cannot assigned null to not nullable type $desiredType")
68
+
69
+ internal class UnexpectedException(val throwable: Throwable) :
70
+ CodedException(throwable)
@@ -0,0 +1,50 @@
1
+ package expo.modules.kotlin.methods
2
+
3
+ import com.facebook.react.bridge.ReadableArray
4
+ import expo.modules.kotlin.Promise
5
+ import expo.modules.kotlin.exception.CodedException
6
+ import expo.modules.kotlin.exception.InvalidArgsNumberException
7
+ import expo.modules.kotlin.exception.UnexpectedException
8
+ import expo.modules.kotlin.iterator
9
+ import expo.modules.kotlin.recycle
10
+ import expo.modules.kotlin.types.AnyType
11
+ import kotlin.jvm.Throws
12
+
13
+ abstract class AnyMethod(
14
+ protected val name: String,
15
+ private val desiredArgsTypes: Array<AnyType>
16
+ ) {
17
+ fun call(args: ReadableArray, promise: Promise) {
18
+ if (desiredArgsTypes.size != args.size()) {
19
+ promise.reject(InvalidArgsNumberException(args.size(), desiredArgsTypes.size))
20
+ return
21
+ }
22
+ try {
23
+ val convertedArgs = convertArgs(args)
24
+ callImplementation(convertedArgs, promise)
25
+ } catch (codedError: CodedException) {
26
+ promise.reject(codedError)
27
+ } catch (e: Throwable) {
28
+ promise.reject(UnexpectedException(e))
29
+ }
30
+ }
31
+
32
+ @Throws(CodedException::class)
33
+ internal abstract fun callImplementation(args: Array<out Any?>, promise: Promise)
34
+
35
+ val argsCount get() = desiredArgsTypes.size
36
+
37
+ @Throws(CodedException::class)
38
+ private fun convertArgs(args: ReadableArray): Array<out Any?> {
39
+ val finalArgs = Array<Any?>(desiredArgsTypes.size) { null }
40
+ val argIterator = args.iterator()
41
+ desiredArgsTypes
42
+ .withIndex()
43
+ .forEach { (index, type) ->
44
+ argIterator.next().recycle {
45
+ finalArgs[index] = type.convert(this)
46
+ }
47
+ }
48
+ return finalArgs
49
+ }
50
+ }
@@ -0,0 +1,14 @@
1
+ package expo.modules.kotlin.methods
2
+
3
+ import expo.modules.kotlin.Promise
4
+ import expo.modules.kotlin.types.AnyType
5
+
6
+ class Method(
7
+ name: String,
8
+ argsType: Array<AnyType>,
9
+ private val body: (args: Array<out Any?>) -> Any?
10
+ ) : AnyMethod(name, argsType) {
11
+ override fun callImplementation(args: Array<out Any?>, promise: Promise) {
12
+ promise.resolve(body(args))
13
+ }
14
+ }
@@ -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
+ }