expo-modules-core 0.6.4 → 0.7.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 (159) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +1 -1
  3. package/android/build.gradle +5 -5
  4. package/android/src/main/java/expo/modules/kotlin/AppContext.kt +11 -1
  5. package/android/src/main/java/expo/modules/kotlin/DynamicExtenstions.kt +5 -3
  6. package/android/src/main/java/expo/modules/kotlin/KPromiseWrapper.kt +8 -2
  7. package/android/src/main/java/expo/modules/kotlin/KotlinInteropModuleRegistry.kt +13 -4
  8. package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +7 -6
  9. package/android/src/main/java/expo/modules/kotlin/ModuleRegistry.kt +6 -1
  10. package/android/src/main/java/expo/modules/kotlin/Promise.kt +1 -1
  11. package/android/src/main/java/expo/modules/kotlin/callbacks/Callback.kt +5 -0
  12. package/android/src/main/java/expo/modules/kotlin/callbacks/ViewCallback.kt +28 -0
  13. package/android/src/main/java/expo/modules/kotlin/callbacks/ViewCallbackDelegate.kt +27 -0
  14. package/android/src/main/java/expo/modules/kotlin/defaultmodules/ErrorManagerModule.kt +25 -0
  15. package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +68 -8
  16. package/android/src/main/java/expo/modules/kotlin/exception/ExceptionDecorator.kt +11 -0
  17. package/android/src/main/java/expo/modules/kotlin/methods/AnyMethod.kt +13 -14
  18. package/android/src/main/java/expo/modules/kotlin/modules/DefinitionMarker.kt +4 -0
  19. package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +3 -2
  20. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +44 -4
  21. package/android/src/main/java/expo/modules/kotlin/records/Record.kt +39 -0
  22. package/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt +14 -7
  23. package/android/src/main/java/expo/modules/kotlin/types/ArrayTypeConverter.kt +11 -5
  24. package/android/src/main/java/expo/modules/kotlin/types/ListTypeConverter.kt +10 -4
  25. package/android/src/main/java/expo/modules/kotlin/types/MapTypeConverter.kt +12 -6
  26. package/android/src/main/java/expo/modules/kotlin/types/PairTypeConverter.kt +29 -13
  27. package/android/src/main/java/expo/modules/kotlin/types/TypeConverter.kt +2 -1
  28. package/android/src/main/java/expo/modules/kotlin/views/CallbacksDefinition.kt +3 -0
  29. package/android/src/main/java/expo/modules/kotlin/views/GroupViewManagerWrapper.kt +22 -0
  30. package/android/src/main/java/expo/modules/kotlin/views/SimpleViewManagerWrapper.kt +22 -0
  31. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinition.kt +27 -2
  32. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinitionBuilder.kt +29 -1
  33. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerWrapperDelegate.kt +59 -2
  34. package/build/EventEmitter.d.ts +1 -0
  35. package/build/EventEmitter.d.ts.map +1 -0
  36. package/build/NativeModulesProxy.d.ts +1 -0
  37. package/build/NativeModulesProxy.d.ts.map +1 -0
  38. package/build/NativeModulesProxy.native.d.ts +1 -0
  39. package/build/NativeModulesProxy.native.d.ts.map +1 -0
  40. package/build/NativeModulesProxy.types.d.ts +1 -0
  41. package/build/NativeModulesProxy.types.d.ts.map +1 -0
  42. package/build/NativeViewManagerAdapter.d.ts +1 -0
  43. package/build/NativeViewManagerAdapter.d.ts.map +1 -0
  44. package/build/NativeViewManagerAdapter.native.d.ts +1 -0
  45. package/build/NativeViewManagerAdapter.native.d.ts.map +1 -0
  46. package/build/PermissionsHook.d.ts +1 -0
  47. package/build/PermissionsHook.d.ts.map +1 -0
  48. package/build/PermissionsInterface.d.ts +1 -0
  49. package/build/PermissionsInterface.d.ts.map +1 -0
  50. package/build/Platform.d.ts +1 -0
  51. package/build/Platform.d.ts.map +1 -0
  52. package/build/SyntheticPlatformEmitter.d.ts +1 -0
  53. package/build/SyntheticPlatformEmitter.d.ts.map +1 -0
  54. package/build/SyntheticPlatformEmitter.web.d.ts +1 -0
  55. package/build/SyntheticPlatformEmitter.web.d.ts.map +1 -0
  56. package/build/deprecate.d.ts +1 -0
  57. package/build/deprecate.d.ts.map +1 -0
  58. package/build/environment/browser.d.ts +1 -0
  59. package/build/environment/browser.d.ts.map +1 -0
  60. package/build/environment/browser.web.d.ts +1 -0
  61. package/build/environment/browser.web.d.ts.map +1 -0
  62. package/build/errors/CodedError.d.ts +1 -0
  63. package/build/errors/CodedError.d.ts.map +1 -0
  64. package/build/errors/UnavailabilityError.d.ts +1 -0
  65. package/build/errors/UnavailabilityError.d.ts.map +1 -0
  66. package/build/index.d.ts +3 -0
  67. package/build/index.d.ts.map +1 -0
  68. package/build/index.js +2 -0
  69. package/build/index.js.map +1 -1
  70. package/build/requireNativeModule.d.ts +16 -0
  71. package/build/requireNativeModule.d.ts.map +1 -0
  72. package/build/requireNativeModule.js +18 -0
  73. package/build/requireNativeModule.js.map +1 -0
  74. package/build/sweet/NativeErrorManager.d.ts +3 -0
  75. package/build/sweet/NativeErrorManager.d.ts.map +1 -0
  76. package/build/sweet/NativeErrorManager.js +3 -0
  77. package/build/sweet/NativeErrorManager.js.map +1 -0
  78. package/build/sweet/setUpErrorManager.fx.d.ts +2 -0
  79. package/build/sweet/setUpErrorManager.fx.d.ts.map +1 -0
  80. package/build/sweet/setUpErrorManager.fx.js +11 -0
  81. package/build/sweet/setUpErrorManager.fx.js.map +1 -0
  82. package/ios/AppDelegates/ExpoAppDelegate.swift +18 -10
  83. package/ios/JSI/ExpoModulesHostObject.h +33 -0
  84. package/ios/JSI/ExpoModulesHostObject.mm +40 -0
  85. package/ios/JSI/ExpoModulesProxySpec.h +4 -0
  86. package/ios/JSI/ExpoModulesProxySpec.mm +1 -3
  87. package/ios/JSI/JSIConversions.h +2 -0
  88. package/ios/JSI/JSIConversions.mm +9 -0
  89. package/ios/JSI/JSIInstaller.h +10 -0
  90. package/ios/JSI/JSIInstaller.mm +14 -2
  91. package/ios/JSI/JavaScriptObject.h +60 -0
  92. package/ios/JSI/JavaScriptObject.mm +93 -0
  93. package/ios/JSI/JavaScriptRuntime.h +54 -0
  94. package/ios/JSI/JavaScriptRuntime.mm +102 -0
  95. package/ios/ModuleRegistryAdapter/EXModuleRegistryAdapter.m +2 -12
  96. package/ios/NativeModulesProxy/EXComponentDataCompatibleWrapper.h +16 -0
  97. package/ios/NativeModulesProxy/EXComponentDataCompatibleWrapper.m +28 -0
  98. package/ios/NativeModulesProxy/EXNativeModulesProxy.mm +90 -66
  99. package/ios/RCTComponentData+Privates.h +12 -0
  100. package/ios/ReactDelegates/ExpoReactDelegate.swift +2 -2
  101. package/ios/ReactDelegates/ExpoReactDelegateHandler.swift +3 -3
  102. package/ios/ReactDelegates/ModulePriorities.swift +1 -1
  103. package/ios/Swift/AppContext.swift +38 -4
  104. package/ios/Swift/Arguments/ArgumentType.swift +4 -0
  105. package/ios/Swift/Arguments/Convertibles.swift +13 -13
  106. package/ios/Swift/Arguments/Types/EnumArgumentType.swift +11 -17
  107. package/ios/Swift/Arguments/Types/PromiseArgumentType.swift +1 -1
  108. package/ios/Swift/Arguments/Types/RawArgumentType.swift +2 -2
  109. package/ios/Swift/Conversions.swift +51 -56
  110. package/ios/Swift/EventListener.swift +8 -10
  111. package/ios/Swift/Events/Callback.swift +66 -0
  112. package/ios/Swift/Events/Event.swift +43 -0
  113. package/ios/Swift/Exceptions/ChainableException.swift +51 -0
  114. package/ios/Swift/{CodedError.swift → Exceptions/CodedError.swift} +1 -12
  115. package/ios/Swift/Exceptions/Exception.swift +62 -0
  116. package/ios/Swift/Exceptions/ExceptionOrigin.swift +28 -0
  117. package/ios/Swift/Exceptions/GenericException.swift +20 -0
  118. package/ios/Swift/Exceptions/UnexpectedException.swift +16 -0
  119. package/ios/Swift/Functions/AnyFunction.swift +11 -1
  120. package/ios/Swift/Functions/ConcreteFunction.swift +37 -16
  121. package/ios/Swift/JavaScriptUtils.swift +43 -0
  122. package/ios/Swift/ModuleHolder.swift +53 -14
  123. package/ios/Swift/ModuleRegistry.swift +4 -1
  124. package/ios/Swift/Modules/AnyModule.swift +0 -1
  125. package/ios/Swift/Modules/ModuleDefinition.swift +4 -13
  126. package/ios/Swift/Modules/ModuleDefinitionBuilder.swift +0 -1
  127. package/ios/Swift/Modules/ModuleDefinitionComponents.swift +0 -188
  128. package/ios/Swift/ModulesProvider.swift +0 -1
  129. package/ios/Swift/Objects/ObjectDefinition.swift +30 -0
  130. package/ios/Swift/Objects/ObjectDefinitionComponents.swift +208 -0
  131. package/ios/Swift/Promise.swift +8 -3
  132. package/ios/Swift/Records/AnyField.swift +7 -0
  133. package/ios/Swift/Records/Field.swift +24 -19
  134. package/ios/Swift/Records/FieldOption.swift +1 -1
  135. package/ios/Swift/Records/Record.swift +12 -4
  136. package/ios/Swift/SwiftInteropBridge.swift +39 -10
  137. package/ios/Swift/Views/AnyViewProp.swift +1 -1
  138. package/ios/Swift/Views/ComponentData.swift +95 -0
  139. package/ios/Swift/Views/ConcreteViewProp.swift +6 -8
  140. package/ios/Swift/Views/ViewFactory.swift +1 -1
  141. package/ios/Swift/Views/ViewManagerDefinition.swift +23 -2
  142. package/ios/Swift/Views/ViewManagerDefinitionBuilder.swift +0 -1
  143. package/ios/Swift/Views/ViewManagerDefinitionComponents.swift +26 -0
  144. package/ios/Swift/Views/ViewModuleWrapper.swift +5 -2
  145. package/ios/Tests/ArgumentTypeSpec.swift +3 -4
  146. package/ios/Tests/ConstantsSpec.swift +4 -4
  147. package/ios/Tests/ConvertiblesSpec.swift +33 -33
  148. package/ios/Tests/ExceptionsSpec.swift +112 -0
  149. package/ios/Tests/FunctionSpec.swift +20 -22
  150. package/ios/Tests/FunctionWithConvertiblesSpec.swift +2 -2
  151. package/ios/Tests/Mocks/ModuleMocks.swift +1 -1
  152. package/ios/Tests/Mocks/ModulesProviderMock.swift +0 -1
  153. package/ios/Tests/ModuleEventListenersSpec.swift +1 -1
  154. package/ios/Tests/RecordSpec.swift +7 -17
  155. package/package.json +3 -3
  156. package/src/index.ts +4 -0
  157. package/src/requireNativeModule.ts +29 -0
  158. package/src/sweet/NativeErrorManager.ts +2 -0
  159. package/src/sweet/setUpErrorManager.fx.ts +12 -0
@@ -1,3 +1,42 @@
1
1
  package expo.modules.kotlin.records
2
2
 
3
+ import android.os.Bundle
4
+ import com.facebook.react.bridge.Arguments
5
+ import com.facebook.react.bridge.ReadableArray
6
+ import com.facebook.react.bridge.ReadableMap
7
+ import java.lang.IllegalArgumentException
8
+ import kotlin.reflect.full.findAnnotation
9
+ import kotlin.reflect.full.memberProperties
10
+ import kotlin.reflect.jvm.isAccessible
11
+
3
12
  interface Record
13
+
14
+ fun Record.toJSMap(): ReadableMap {
15
+ val writableMap = Arguments.createMap()
16
+ javaClass
17
+ .kotlin
18
+ .memberProperties.map { property ->
19
+ val fieldInformation = property.findAnnotation<Field>() ?: return@map
20
+ val jsKey = fieldInformation.key.takeUnless { it == "" } ?: property.name
21
+
22
+ property.isAccessible = true
23
+
24
+ // TODO(@lukmccall): add more sophisticated conversion method
25
+ when (val value = property.get(this)) {
26
+ null, is Unit -> writableMap.putNull(jsKey)
27
+ is Boolean -> writableMap.putBoolean(jsKey, value)
28
+ is Int -> writableMap.putInt(jsKey, value)
29
+ is Number -> writableMap.putDouble(jsKey, value.toDouble())
30
+ is String -> writableMap.putString(jsKey, value)
31
+ is ReadableArray -> writableMap.putArray(jsKey, value)
32
+ is ReadableMap -> writableMap.putMap(jsKey, value)
33
+ is Record -> writableMap.putMap(jsKey, value.toJSMap())
34
+ is Bundle -> writableMap.putMap(jsKey, Arguments.fromBundle(value))
35
+ is List<*> -> writableMap.putArray(jsKey, Arguments.fromList(value))
36
+ is Array<*> -> writableMap.putArray(jsKey, Arguments.fromArray(value))
37
+ else -> throw IllegalArgumentException("Could not convert " + value.javaClass)
38
+ }
39
+ }
40
+
41
+ return writableMap
42
+ }
@@ -3,6 +3,10 @@ package expo.modules.kotlin.records
3
3
  import com.facebook.react.bridge.Dynamic
4
4
  import expo.modules.kotlin.allocators.ObjectConstructor
5
5
  import expo.modules.kotlin.allocators.ObjectConstructorFactory
6
+ import expo.modules.kotlin.exception.FieldCastException
7
+ import expo.modules.kotlin.exception.RecordCastException
8
+ import expo.modules.kotlin.exception.exceptionDecorator
9
+ import expo.modules.kotlin.recycle
6
10
  import expo.modules.kotlin.types.TypeConverter
7
11
  import expo.modules.kotlin.types.TypeConverterProvider
8
12
  import kotlin.reflect.KClass
@@ -18,7 +22,7 @@ class RecordTypeConverter<T : Record>(
18
22
  ) : TypeConverter<T>(type.isMarkedNullable) {
19
23
  private val objectConstructorFactory = ObjectConstructorFactory()
20
24
 
21
- override fun convertNonOptional(value: Dynamic): T {
25
+ override fun convertNonOptional(value: Dynamic): T = exceptionDecorator({ cause -> RecordCastException(type, cause) }) {
22
26
  val jsMap = value.asMap()
23
27
 
24
28
  val kClass = type.classifier as KClass<*>
@@ -35,14 +39,17 @@ class RecordTypeConverter<T : Record>(
35
39
  return@map
36
40
  }
37
41
 
38
- val value = jsMap.getDynamic(jsKey)
39
- val javaField = property.javaField!!
42
+ jsMap.getDynamic(jsKey).recycle {
43
+ val javaField = property.javaField!!
40
44
 
41
- val elementConverter = converterProvider.obtainTypeConverter(property.returnType)
42
- val casted = elementConverter.convert(value)
45
+ val elementConverter = converterProvider.obtainTypeConverter(property.returnType)
46
+ val casted = exceptionDecorator({ cause -> FieldCastException(property.name, property.returnType, type, cause) }) {
47
+ elementConverter.convert(this)
48
+ }
43
49
 
44
- javaField.isAccessible = true
45
- javaField.set(instance, casted)
50
+ javaField.isAccessible = true
51
+ javaField.set(instance, casted)
52
+ }
46
53
  }
47
54
 
48
55
  @Suppress("UNCHECKED_CAST")
@@ -1,16 +1,18 @@
1
1
  package expo.modules.kotlin.types
2
2
 
3
3
  import com.facebook.react.bridge.Dynamic
4
+ import expo.modules.kotlin.exception.CollectionElementCastException
5
+ import expo.modules.kotlin.exception.exceptionDecorator
4
6
  import expo.modules.kotlin.recycle
5
7
  import kotlin.reflect.KClass
6
8
  import kotlin.reflect.KType
7
9
 
8
10
  class ArrayTypeConverter(
9
11
  converterProvider: TypeConverterProvider,
10
- private val type: KType,
11
- ) : TypeConverter<Array<*>>(type.isMarkedNullable) {
12
+ private val arrayType: KType,
13
+ ) : TypeConverter<Array<*>>(arrayType.isMarkedNullable) {
12
14
  private val arrayElementConverter = converterProvider.obtainTypeConverter(
13
- requireNotNull(type.arguments.first().type) {
15
+ requireNotNull(arrayType.arguments.first().type) {
14
16
  "The array type should contain the type of the elements."
15
17
  }
16
18
  )
@@ -22,7 +24,11 @@ class ArrayTypeConverter(
22
24
  array[i] = jsArray
23
25
  .getDynamic(i)
24
26
  .recycle {
25
- arrayElementConverter.convert(this)
27
+ exceptionDecorator({ cause ->
28
+ CollectionElementCastException(arrayType, arrayType.arguments.first().type!!, type, cause)
29
+ }) {
30
+ arrayElementConverter.convert(this)
31
+ }
26
32
  }
27
33
  }
28
34
  return array
@@ -37,7 +43,7 @@ class ArrayTypeConverter(
37
43
  @Suppress("UNCHECKED_CAST")
38
44
  private fun createTypedArray(size: Int): Array<Any?> {
39
45
  return java.lang.reflect.Array.newInstance(
40
- (type.arguments.first().type!!.classifier as KClass<*>).java,
46
+ (arrayType.arguments.first().type!!.classifier as KClass<*>).java,
41
47
  size
42
48
  ) as Array<Any?>
43
49
  }
@@ -1,15 +1,17 @@
1
1
  package expo.modules.kotlin.types
2
2
 
3
3
  import com.facebook.react.bridge.Dynamic
4
+ import expo.modules.kotlin.exception.CollectionElementCastException
5
+ import expo.modules.kotlin.exception.exceptionDecorator
4
6
  import expo.modules.kotlin.recycle
5
7
  import kotlin.reflect.KType
6
8
 
7
9
  class ListTypeConverter(
8
10
  converterProvider: TypeConverterProvider,
9
- type: KType,
10
- ) : TypeConverter<List<*>>(type.isMarkedNullable) {
11
+ private val listType: KType,
12
+ ) : TypeConverter<List<*>>(listType.isMarkedNullable) {
11
13
  private val elementConverter = converterProvider.obtainTypeConverter(
12
- requireNotNull(type.arguments.first().type) {
14
+ requireNotNull(listType.arguments.first().type) {
13
15
  "The list type should contain the type of elements."
14
16
  }
15
17
  )
@@ -18,7 +20,11 @@ class ListTypeConverter(
18
20
  val jsArray = value.asArray()
19
21
  return List(jsArray.size()) { index ->
20
22
  jsArray.getDynamic(index).recycle {
21
- elementConverter.convert(this)
23
+ exceptionDecorator({ cause ->
24
+ CollectionElementCastException(listType, listType.arguments.first().type!!, type, cause)
25
+ }) {
26
+ elementConverter.convert(this)
27
+ }
22
28
  }
23
29
  }
24
30
  }
@@ -4,22 +4,24 @@ package expo.modules.kotlin.types
4
4
 
5
5
  import com.facebook.react.bridge.Dynamic
6
6
  import com.facebook.react.bridge.DynamicFromObject
7
+ import expo.modules.kotlin.exception.CollectionElementCastException
8
+ import expo.modules.kotlin.exception.exceptionDecorator
7
9
  import expo.modules.kotlin.recycle
8
10
  import kotlin.reflect.KType
9
11
  import kotlin.reflect.typeOf
10
12
 
11
13
  class MapTypeConverter(
12
14
  converterProvider: TypeConverterProvider,
13
- type: KType
14
- ) : TypeConverter<Map<*, *>>(type.isMarkedNullable) {
15
+ private val mapType: KType
16
+ ) : TypeConverter<Map<*, *>>(mapType.isMarkedNullable) {
15
17
  init {
16
- require(type.arguments.first().type == typeOf<String>()) {
17
- "The map key type should be String, but received ${type.arguments.first()}."
18
+ require(mapType.arguments.first().type == typeOf<String>()) {
19
+ "The map key type should be String, but received ${mapType.arguments.first()}."
18
20
  }
19
21
  }
20
22
 
21
23
  private val valueConverter = converterProvider.obtainTypeConverter(
22
- requireNotNull(type.arguments.getOrNull(1)?.type) {
24
+ requireNotNull(mapType.arguments.getOrNull(1)?.type) {
23
25
  "The map type should contain the key type."
24
26
  }
25
27
  )
@@ -30,7 +32,11 @@ class MapTypeConverter(
30
32
 
31
33
  jsMap.entryIterator.forEach { (key, value) ->
32
34
  DynamicFromObject(value).recycle {
33
- result[key] = valueConverter.convert(this)
35
+ exceptionDecorator({ cause ->
36
+ CollectionElementCastException(mapType, mapType.arguments[1].type!!, type, cause)
37
+ }) {
38
+ result[key] = valueConverter.convert(this)
39
+ }
34
40
  }
35
41
  }
36
42
 
@@ -1,28 +1,44 @@
1
1
  package expo.modules.kotlin.types
2
2
 
3
3
  import com.facebook.react.bridge.Dynamic
4
+ import com.facebook.react.bridge.ReadableArray
5
+ import expo.modules.kotlin.exception.CollectionElementCastException
6
+ import expo.modules.kotlin.exception.exceptionDecorator
7
+ import expo.modules.kotlin.recycle
4
8
  import kotlin.reflect.KType
5
9
 
6
10
  class PairTypeConverter(
7
11
  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
- }
12
+ private val pairType: KType,
13
+ ) : TypeConverter<Pair<*, *>>(pairType.isMarkedNullable) {
14
+ private val converters = listOf(
15
+ converterProvider.obtainTypeConverter(
16
+ requireNotNull(pairType.arguments.getOrNull(0)?.type) {
17
+ "The pair type should contain the type of the first parameter."
18
+ }
19
+ ),
20
+ converterProvider.obtainTypeConverter(
21
+ requireNotNull(pairType.arguments.getOrNull(1)?.type) {
22
+ "The pair type should contain the type of the second parameter."
23
+ }
24
+ )
19
25
  )
20
26
 
21
27
  override fun convertNonOptional(value: Dynamic): Pair<*, *> {
22
28
  val jsArray = value.asArray()
23
29
  return Pair(
24
- firstConverter.convert(jsArray.getDynamic(0)),
25
- secondConverter.convert(jsArray.getDynamic(1)),
30
+ convertElement(jsArray, 0),
31
+ convertElement(jsArray, 1)
26
32
  )
27
33
  }
34
+
35
+ private fun convertElement(array: ReadableArray, index: Int): Any? {
36
+ return array.getDynamic(index).recycle {
37
+ exceptionDecorator({ cause ->
38
+ CollectionElementCastException(pairType, pairType.arguments[index].type!!, type, cause)
39
+ }) {
40
+ converters[index].convert(this)
41
+ }
42
+ }
43
+ }
28
44
  }
@@ -1,6 +1,7 @@
1
1
  package expo.modules.kotlin.types
2
2
 
3
3
  import com.facebook.react.bridge.Dynamic
4
+ import expo.modules.kotlin.exception.NullArgumentException
4
5
 
5
6
  abstract class TypeConverter<Type : Any>(
6
7
  private val isOptional: Boolean
@@ -10,7 +11,7 @@ abstract class TypeConverter<Type : Any>(
10
11
  if (isOptional) {
11
12
  return null
12
13
  }
13
- throw IllegalArgumentException()
14
+ throw NullArgumentException()
14
15
  }
15
16
  return convertNonOptional(value)
16
17
  }
@@ -0,0 +1,3 @@
1
+ package expo.modules.kotlin.views
2
+
3
+ class CallbacksDefinition(val names: Array<out String>)
@@ -3,6 +3,7 @@ package expo.modules.kotlin.views
3
3
  import android.view.View
4
4
  import android.view.ViewGroup
5
5
  import com.facebook.react.bridge.ReadableMap
6
+ import com.facebook.react.common.MapBuilder
6
7
  import com.facebook.react.uimanager.ThemedReactContext
7
8
  import com.facebook.react.uimanager.ViewGroupManager
8
9
  import com.facebook.react.uimanager.annotations.ReactProp
@@ -19,4 +20,25 @@ class GroupViewManagerWrapper(
19
20
  fun setProxiedProperties(view: View, proxiedProperties: ReadableMap) {
20
21
  viewWrapperDelegate.setProxiedProperties(view, proxiedProperties)
21
22
  }
23
+
24
+ override fun onDropViewInstance(view: ViewGroup) {
25
+ super.onDropViewInstance(view)
26
+ viewWrapperDelegate.onDestroy(view)
27
+ }
28
+
29
+ override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any>? {
30
+ viewWrapperDelegate.getExportedCustomDirectEventTypeConstants()?.let {
31
+ val directEvents = super.getExportedCustomDirectEventTypeConstants() ?: emptyMap()
32
+ val builder = MapBuilder.builder<String, Any>()
33
+ directEvents.forEach { event ->
34
+ builder.put(event.key, event.value)
35
+ }
36
+ it.forEach { event ->
37
+ builder.put(event.key, event.value)
38
+ }
39
+ return builder.build()
40
+ }
41
+
42
+ return super.getExportedCustomDirectEventTypeConstants()
43
+ }
22
44
  }
@@ -2,6 +2,7 @@ package expo.modules.kotlin.views
2
2
 
3
3
  import android.view.View
4
4
  import com.facebook.react.bridge.ReadableMap
5
+ import com.facebook.react.common.MapBuilder
5
6
  import com.facebook.react.uimanager.SimpleViewManager
6
7
  import com.facebook.react.uimanager.ThemedReactContext
7
8
  import com.facebook.react.uimanager.annotations.ReactProp
@@ -18,4 +19,25 @@ class SimpleViewManagerWrapper(
18
19
  fun setProxiedProperties(view: View, proxiedProperties: ReadableMap) {
19
20
  viewWrapperDelegate.setProxiedProperties(view, proxiedProperties)
20
21
  }
22
+
23
+ override fun onDropViewInstance(view: View) {
24
+ super.onDropViewInstance(view)
25
+ viewWrapperDelegate.onDestroy(view)
26
+ }
27
+
28
+ override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any>? {
29
+ viewWrapperDelegate.getExportedCustomDirectEventTypeConstants()?.let {
30
+ val directEvents = super.getExportedCustomDirectEventTypeConstants() ?: emptyMap()
31
+ val builder = MapBuilder.builder<String, Any>()
32
+ directEvents.forEach { event ->
33
+ builder.put(event.key, event.value)
34
+ }
35
+ it.forEach { event ->
36
+ builder.put(event.key, event.value)
37
+ }
38
+ return builder.build()
39
+ }
40
+
41
+ return super.getExportedCustomDirectEventTypeConstants()
42
+ }
21
43
  }
@@ -4,14 +4,20 @@ import android.content.Context
4
4
  import android.util.Log
5
5
  import android.view.View
6
6
  import android.view.ViewGroup
7
+ import com.facebook.react.bridge.ReactContext
7
8
  import com.facebook.react.bridge.ReadableMap
9
+ import expo.modules.adapters.react.NativeModulesProxy
8
10
  import expo.modules.core.ViewManager
11
+ import expo.modules.kotlin.exception.CodedException
12
+ import expo.modules.kotlin.exception.UnexpectedException
9
13
  import expo.modules.kotlin.recycle
10
14
 
11
15
  class ViewManagerDefinition(
12
16
  private val viewFactory: (Context) -> View,
13
17
  private val viewType: Class<out View>,
14
- private val props: Map<String, AnyViewProp>
18
+ private val props: Map<String, AnyViewProp>,
19
+ val onViewDestroys: ((View) -> Unit)? = null,
20
+ val callbacksDefinition: CallbacksDefinition? = null
15
21
  ) {
16
22
 
17
23
  fun createView(context: Context): View = viewFactory(context)
@@ -33,9 +39,28 @@ class ViewManagerDefinition(
33
39
  try {
34
40
  propDelegate.set(this, onView)
35
41
  } catch (exception: Throwable) {
36
- Log.e("ExpoModulesCore", "Cannot set the $key prop on the ${viewType.simpleName}.", exception)
42
+ Log.e("ExpoModulesCore", "Cannot set the '$key' prop on the '${viewType.simpleName}'.", exception)
43
+
44
+ handleException(
45
+ onView,
46
+ when (exception) {
47
+ is CodedException -> exception
48
+ else -> UnexpectedException(exception)
49
+ }
50
+ )
37
51
  }
38
52
  }
39
53
  }
40
54
  }
55
+
56
+ fun handleException(view: View, exception: CodedException) {
57
+ val reactContext = (view.context as? ReactContext) ?: return
58
+ val nativeModulesProxy = reactContext
59
+ .catalystInstance
60
+ ?.getNativeModule("NativeUnimoduleProxy") as? NativeModulesProxy
61
+ ?: return
62
+ val appContext = nativeModulesProxy.kotlinInteropModuleRegistry.appContext
63
+
64
+ appContext.errorManager?.reportExceptionToLogBox(exception)
65
+ }
41
66
  }
@@ -4,9 +4,11 @@ package expo.modules.kotlin.views
4
4
 
5
5
  import android.content.Context
6
6
  import android.view.View
7
+ import expo.modules.kotlin.modules.DefinitionMarker
7
8
  import expo.modules.kotlin.types.toAnyType
8
9
  import kotlin.reflect.typeOf
9
10
 
11
+ @DefinitionMarker
10
12
  class ViewManagerDefinitionBuilder {
11
13
  @PublishedApi
12
14
  internal var viewFactory: ((Context) -> View)? = null
@@ -14,19 +16,38 @@ class ViewManagerDefinitionBuilder {
14
16
  internal var viewType: Class<out View>? = null
15
17
  @PublishedApi
16
18
  internal var props = mutableMapOf<String, AnyViewProp>()
19
+ @PublishedApi
20
+ internal var onViewDestroys: ((View) -> Unit)? = null
21
+
22
+ private var callbacksDefinition: CallbacksDefinition? = null
17
23
 
18
24
  fun build(): ViewManagerDefinition =
19
25
  ViewManagerDefinition(
20
26
  requireNotNull(viewFactory),
21
27
  requireNotNull(viewType),
22
- props
28
+ props,
29
+ onViewDestroys,
30
+ callbacksDefinition
23
31
  )
24
32
 
33
+ /**
34
+ * Defines the factory creating a native view when the module is used as a view.
35
+ */
25
36
  inline fun <reified ViewType : View> view(noinline body: (Context) -> ViewType) {
26
37
  viewType = ViewType::class.java
27
38
  viewFactory = body
28
39
  }
29
40
 
41
+ /**
42
+ * Creates view's lifecycle listener that is called right after the view isn't longer used by React Native.
43
+ */
44
+ inline fun <reified ViewType : View> onViewDestroys(noinline body: (view: ViewType) -> Unit) {
45
+ onViewDestroys = { body(it as ViewType) }
46
+ }
47
+
48
+ /**
49
+ * Creates a view prop that defines its name and setter.
50
+ */
30
51
  inline fun <reified ViewType : View, reified PropType> prop(
31
52
  name: String,
32
53
  noinline body: (view: ViewType, prop: PropType) -> Unit
@@ -37,4 +58,11 @@ class ViewManagerDefinitionBuilder {
37
58
  body
38
59
  )
39
60
  }
61
+
62
+ /**
63
+ * Defines prop names that should be treated as callbacks.
64
+ */
65
+ fun events(vararg callbacks: String) {
66
+ callbacksDefinition = CallbacksDefinition(callbacks)
67
+ }
40
68
  }
@@ -1,9 +1,15 @@
1
1
  package expo.modules.kotlin.views
2
2
 
3
3
  import android.content.Context
4
+ import android.util.Log
4
5
  import android.view.View
5
6
  import com.facebook.react.bridge.ReadableMap
7
+ import com.facebook.react.common.MapBuilder
8
+ import expo.modules.core.utilities.ifNull
6
9
  import expo.modules.kotlin.ModuleHolder
10
+ import expo.modules.kotlin.callbacks.ViewCallbackDelegate
11
+ import kotlin.reflect.full.declaredMemberProperties
12
+ import kotlin.reflect.jvm.isAccessible
7
13
 
8
14
  class ViewManagerWrapperDelegate(internal var moduleHolder: ModuleHolder) {
9
15
  private val definition: ViewManagerDefinition
@@ -12,10 +18,61 @@ class ViewManagerWrapperDelegate(internal var moduleHolder: ModuleHolder) {
12
18
  val name: String
13
19
  get() = moduleHolder.name
14
20
 
15
- fun createView(context: Context): View =
16
- definition.createView(context)
21
+ fun createView(context: Context): View {
22
+ return definition
23
+ .createView(context)
24
+ .also {
25
+ configureView(it)
26
+ }
27
+ }
17
28
 
18
29
  fun setProxiedProperties(view: View, proxiedProperties: ReadableMap) {
19
30
  definition.setProps(proxiedProperties, view)
20
31
  }
32
+
33
+ fun onDestroy(view: View) =
34
+ definition.onViewDestroys?.invoke(view)
35
+
36
+ fun getExportedCustomDirectEventTypeConstants(): Map<String, Any>? {
37
+ val builder = MapBuilder.builder<String, Any>()
38
+ definition
39
+ .callbacksDefinition
40
+ ?.names
41
+ ?.forEach {
42
+ builder.put(
43
+ it, MapBuilder.of<String, Any>("registrationName", it)
44
+ )
45
+ }
46
+ return builder.build()
47
+ }
48
+
49
+ private fun configureView(view: View) {
50
+ val callbacks = definition.callbacksDefinition?.names ?: return
51
+
52
+ val kClass = view.javaClass.kotlin
53
+ val propertiesMap = kClass
54
+ .declaredMemberProperties
55
+ .map { it.name to it }
56
+ .toMap()
57
+
58
+ callbacks.forEach {
59
+ val property = propertiesMap[it].ifNull {
60
+ Log.w("ExpoModuleCore", "Property `$it` does not exist in ${kClass.simpleName}.")
61
+ return@forEach
62
+ }
63
+ property.isAccessible = true
64
+
65
+ val delegate = property.getDelegate(view).ifNull {
66
+ Log.w("ExpoModulesCore", "Property delegate for `$it` in ${kClass.simpleName} does not exist.")
67
+ return@forEach
68
+ }
69
+
70
+ val viewDelegate = (delegate as? ViewCallbackDelegate<*>).ifNull {
71
+ Log.w("ExpoModulesCore", "Property delegate for `$it` cannot be cased to `ViewCallbackDelegate`.")
72
+ return@forEach
73
+ }
74
+
75
+ viewDelegate.isValidated = true
76
+ }
77
+ }
21
78
  }
@@ -22,3 +22,4 @@ export declare class EventEmitter {
22
22
  emit(eventName: string, ...params: any[]): void;
23
23
  }
24
24
  export {};
25
+ //# sourceMappingURL=EventEmitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventEmitter.d.ts","sourceRoot":"","sources":["../src/EventEmitter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAY,MAAM,cAAc,CAAC;AAI5D,aAAK,YAAY,GAAG;IAClB,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C,CAAC;AAGF,oBAAY,YAAY,GAAG;IACzB;;OAEG;IACH,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB,CAAC;AAEF,qBAAa,YAAY;IACvB,cAAc,SAAK;IACnB,aAAa,EAAE,YAAY,CAAC;IAC5B,aAAa,EAAE,kBAAkB,CAAC;gBAEtB,YAAY,EAAE,YAAY;IAKtC,WAAW,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,YAAY;IAgB7E,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAmB3C,kBAAkB,CAAC,YAAY,EAAE,YAAY,GAAG,IAAI;IA0BpD,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI;CAGhD"}
@@ -3,3 +3,4 @@ declare const _default: {
3
3
  [moduleName: string]: ProxyNativeModule;
4
4
  };
5
5
  export default _default;
6
+ //# sourceMappingURL=NativeModulesProxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NativeModulesProxy.d.ts","sourceRoot":"","sources":["../src/NativeModulesProxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;;;;AAG/D,wBAAiE"}
@@ -7,3 +7,4 @@ declare const NativeModulesProxy: {
7
7
  */
8
8
  export declare function useExpoTurboModules(state?: boolean): void;
9
9
  export default NativeModulesProxy;
10
+ //# sourceMappingURL=NativeModulesProxy.native.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NativeModulesProxy.native.d.ts","sourceRoot":"","sources":["../src/NativeModulesProxy.native.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAA0B,MAAM,4BAA4B,CAAC;AAWvF,QAAA,MAAM,kBAAkB,EAAE;IAAE,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,CAAA;CAAO,CAAC;AAK3E;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,GAAE,OAAc,QAExD;AAuDD,eAAe,kBAAkB,CAAC"}
@@ -6,3 +6,4 @@ export declare type ProxyNativeModule = {
6
6
  export declare type TurboNativeModuleProxy = {
7
7
  callMethodAsync: <ReturnType>(moduleName: string, methodName: string, args: any[]) => Promise<ReturnType>;
8
8
  };
9
+ //# sourceMappingURL=NativeModulesProxy.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NativeModulesProxy.types.d.ts","sourceRoot":"","sources":["../src/NativeModulesProxy.types.ts"],"names":[],"mappings":"AAAA,oBAAY,iBAAiB,GAAG;IAC9B,CAAC,YAAY,EAAE,MAAM,GAAG,GAAG,CAAC;IAC5B,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C,CAAC;AAEF,oBAAY,sBAAsB,GAAG;IACnC,eAAe,EAAE,CAAC,UAAU,EAC1B,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,GAAG,EAAE,KACR,OAAO,CAAC,UAAU,CAAC,CAAC;CAC1B,CAAC"}
@@ -1,2 +1,3 @@
1
1
  import React from 'react';
2
2
  export declare function requireNativeViewManager<P = any>(viewName: string): React.ComponentType<P>;
3
+ //# sourceMappingURL=NativeViewManagerAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NativeViewManagerAdapter.d.ts","sourceRoot":"","sources":["../src/NativeViewManagerAdapter.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,wBAAgB,wBAAwB,CAAC,CAAC,GAAG,GAAG,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAE1F"}
@@ -3,3 +3,4 @@ import React from 'react';
3
3
  * A drop-in replacement for `requireNativeComponent`.
4
4
  */
5
5
  export declare function requireNativeViewManager<P = any>(viewName: string): React.ComponentType<P>;
6
+ //# sourceMappingURL=NativeViewManagerAdapter.native.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NativeViewManagerAdapter.native.d.ts","sourceRoot":"","sources":["../src/NativeViewManagerAdapter.native.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAqB1B;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,GAAG,GAAG,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAqC1F"}
@@ -20,3 +20,4 @@ export declare type PermissionHookOptions<Options extends object> = PermissionHo
20
20
  */
21
21
  export declare function createPermissionHook<Permission extends PermissionResponse, Options extends object>(methods: PermissionHookMethods<Permission, Options>): (options?: PermissionHookOptions<Options> | undefined) => [Permission | null, RequestPermissionMethod<Permission>, GetPermissionMethod<Permission>];
22
22
  export {};
23
+ //# sourceMappingURL=PermissionsHook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PermissionsHook.d.ts","sourceRoot":"","sources":["../src/PermissionsHook.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAG5D,aAAK,uBAAuB,CAAC,UAAU,SAAS,kBAAkB,IAAI,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;AAChG,aAAK,mBAAmB,CAAC,UAAU,SAAS,kBAAkB,IAAI,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;AAE5F,UAAU,qBAAqB,CAAC,UAAU,SAAS,kBAAkB,EAAE,OAAO,GAAG,KAAK;IACpF,wEAAwE;IACxE,aAAa,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IAC1D,6EAA6E;IAC7E,SAAS,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;CACvD;AAED,UAAU,sBAAsB;IAC9B,qGAAqG;IACrG,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,6EAA6E;IAC7E,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,oBAAY,qBAAqB,CAAC,OAAO,SAAS,MAAM,IAAI,sBAAsB,GAAG,OAAO,CAAC;AAkD7F;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,SAAS,kBAAkB,EAAE,OAAO,SAAS,MAAM,EAChG,OAAO,EAAE,qBAAqB,CAAC,UAAU,EAAE,OAAO,CAAC,uJAIpD"}
@@ -10,3 +10,4 @@ export interface PermissionResponse {
10
10
  granted: boolean;
11
11
  canAskAgain: boolean;
12
12
  }
13
+ //# sourceMappingURL=PermissionsInterface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PermissionsInterface.d.ts","sourceRoot":"","sources":["../src/PermissionsInterface.ts"],"names":[],"mappings":"AAAA,oBAAY,gBAAgB;IAC1B,OAAO,YAAY;IACnB,YAAY,iBAAiB;IAC7B,MAAM,WAAW;CAClB;AAED,oBAAY,oBAAoB,GAAG,OAAO,GAAG,MAAM,CAAC;AAEpD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,gBAAgB,CAAC;IACzB,OAAO,EAAE,oBAAoB,CAAC;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;CACtB"}
@@ -42,3 +42,4 @@ declare const Platform: {
42
42
  isAsyncDebugging: boolean;
43
43
  };
44
44
  export default Platform;
45
+ //# sourceMappingURL=Platform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Platform.d.ts","sourceRoot":"","sources":["../src/Platform.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,cAAc,EAAE,MAAM,cAAc,CAAC;AAS/E,oBAAY,oBAAoB,GAAG,cAAc,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;AAEtF,oBAAY,cAAc,GAAG,CAAC,CAAC,EAAE,SAAS,EAAE;KAAG,QAAQ,IAAI,oBAAoB,CAAC,CAAC,EAAE,CAAC;CAAE,KAAK,CAAC,CAAC;AAE7F,QAAA,MAAM,QAAQ;IACZ;;;OAGG;;IAEH;;;;;;;OAOG;;IAEH;;;OAGG;;IAEH;;;;OAIG;;IAEH;;;;OAIG;;IAEH;;;OAGG;;CAEJ,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -3,3 +3,4 @@
3
3
  * registered in the API layer with `NativeEventEmitter`.
4
4
  */
5
5
  export { default } from 'react-native/Libraries/EventEmitter/RCTDeviceEventEmitter';
6
+ //# sourceMappingURL=SyntheticPlatformEmitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SyntheticPlatformEmitter.d.ts","sourceRoot":"","sources":["../src/SyntheticPlatformEmitter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,2DAA2D,CAAC"}