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
package/CHANGELOG.md CHANGED
@@ -10,6 +10,26 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 0.7.0 — 2022-01-26
14
+
15
+ ### 🎉 New features
16
+
17
+ - Allow accessing `RCTBridge` from the modules on iOS. ([#15816](https://github.com/expo/expo/pull/15816) by [@tsapeta](https://github.com/tsapeta))
18
+ - Added support for native callbacks through the view props in Sweet API on iOS. ([#15731](https://github.com/expo/expo/pull/15731) by [@tsapeta](https://github.com/tsapeta))
19
+ - Added support for native callbacks through the view props in Sweet API on Android. ([#15743](https://github.com/expo/expo/pull/15743) by [@lukmccall](https://github.com/lukmccall))
20
+ - The `ModuleDefinition` will use class name if the `name` component wasn't provided in Sweet API on Android. ([#15738](https://github.com/expo/expo/pull/15738) by [@lukmccall](https://github.com/lukmccall))
21
+ - Added `onViewDestroys` component to the `ViewManager` in Sweet API on Android. ([#15740](https://github.com/expo/expo/pull/15740) by [@lukmccall](https://github.com/lukmccall))
22
+ - Added shortened `constants` component that takes `vargs Pair<String, Any?>` as an argument in Sweet API on Android. ([#15742](https://github.com/expo/expo/pull/15742) by [@lukmccall](https://github.com/lukmccall))
23
+ - Introduced the concept of chainable exceptions in Sweet API on iOS. ([#15813](https://github.com/expo/expo/pull/15813) by [@tsapeta](https://github.com/tsapeta))
24
+ - Sweet function closures can throw errors on iOS. ([#15849](https://github.com/expo/expo/pull/15849) by [@tsapeta](https://github.com/tsapeta))
25
+ - Add `requireNativeModule` function to replace accessing native modules from `NativeModulesProxy`. ([#15848](https://github.com/expo/expo/pull/15848) by [@tsapeta](https://github.com/tsapeta))
26
+ - Implemented basic functionality of JSI host object to replace `NativeModulesProxy` on iOS. ([#15847](https://github.com/expo/expo/pull/15847) by [@tsapeta](https://github.com/tsapeta))
27
+
28
+ ### 🐛 Bug fixes
29
+
30
+ - It's no longer possible to directly call methods from the `ModuleDefinition` in the `ViewManagers` on Android. ([#15741](https://github.com/expo/expo/pull/15741) by [@lukmccall](https://github.com/lukmccall))
31
+ - Fix compatibility with react-native 0.66. ([#15914](https://github.com/expo/expo/pull/15914) by [@kudo](https://github.com/kudo))
32
+
13
33
  ## 0.6.4 — 2022-01-05
14
34
 
15
35
  ### 🐛 Bug fixes
package/README.md CHANGED
@@ -4,7 +4,7 @@ The core of Expo Modules architecture.
4
4
 
5
5
  # Installation in managed Expo projects
6
6
 
7
- For managed [managed](https://docs.expo.io/versions/latest/introduction/managed-vs-bare/) Expo projects, please follow the installation instructions in the [API documentation for the latest stable release](#api-documentation). If you follow the link and there is no documentation available then this library is not yet usable within managed projects &mdash; it is likely to be included in an upcoming Expo SDK release.
7
+ For [managed](https://docs.expo.io/versions/latest/introduction/managed-vs-bare/) Expo projects, please follow the installation instructions in the [API documentation for the latest stable release](#api-documentation). If you follow the link and there is no documentation available then this library is not yet usable within managed projects &mdash; it is likely to be included in an upcoming Expo SDK release.
8
8
 
9
9
  # Installation in bare React Native projects
10
10
 
@@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
3
3
  apply plugin: 'maven'
4
4
 
5
5
  group = 'host.exp.exponent'
6
- version = '0.6.4'
6
+ version = '0.7.0'
7
7
 
8
8
  buildscript {
9
9
  // Simple helper that allows the root project to override versions declared by this library.
@@ -16,7 +16,7 @@ buildscript {
16
16
  }
17
17
 
18
18
  dependencies {
19
- classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${safeExtGet('kotlinVersion', '1.4.21')}")
19
+ classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${safeExtGet('kotlinVersion', '1.6.10')}")
20
20
  }
21
21
  }
22
22
 
@@ -58,7 +58,7 @@ android {
58
58
  targetSdkVersion safeExtGet("targetSdkVersion", 30)
59
59
  consumerProguardFiles 'proguard-rules.pro'
60
60
  versionCode 1
61
- versionName "0.6.4"
61
+ versionName "0.7.0"
62
62
  }
63
63
  lintOptions {
64
64
  abortOnError false
@@ -81,8 +81,8 @@ android {
81
81
  }
82
82
 
83
83
  dependencies {
84
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${safeExtGet('kotlinVersion', '1.4.21')}"
85
- implementation "org.jetbrains.kotlin:kotlin-reflect:${safeExtGet('kotlinVersion', '1.4.21')}"
84
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${safeExtGet('kotlinVersion', '1.6.10')}"
85
+ implementation "org.jetbrains.kotlin:kotlin-reflect:${safeExtGet('kotlinVersion', '1.6.10')}"
86
86
  implementation 'androidx.annotation:annotation:1.2.0'
87
87
 
88
88
  // used only in `expo.modules.core.errors.ModuleDestroyedException` for API export
@@ -15,6 +15,7 @@ import expo.modules.interfaces.imageloader.ImageLoaderInterface
15
15
  import expo.modules.interfaces.permissions.Permissions
16
16
  import expo.modules.interfaces.sensors.SensorServiceInterface
17
17
  import expo.modules.interfaces.taskManager.TaskManagerInterface
18
+ import expo.modules.kotlin.defaultmodules.ErrorManagerModule
18
19
  import expo.modules.kotlin.events.EventName
19
20
  import expo.modules.kotlin.events.KEventEmitterWrapper
20
21
  import expo.modules.kotlin.events.OnActivityResultPayload
@@ -26,7 +27,10 @@ class AppContext(
26
27
  val legacyModuleRegistry: expo.modules.core.ModuleRegistry,
27
28
  private val reactContextHolder: WeakReference<ReactApplicationContext>
28
29
  ) {
29
- val registry = ModuleRegistry(WeakReference(this)).register(modulesProvider)
30
+ val registry = ModuleRegistry(WeakReference(this)).apply {
31
+ register(ErrorManagerModule())
32
+ register(modulesProvider)
33
+ }
30
34
  private val reactLifecycleDelegate = ReactLifecycleDelegate(this)
31
35
 
32
36
  init {
@@ -128,6 +132,12 @@ class AppContext(
128
132
  )
129
133
  }
130
134
 
135
+ internal val callbackInvoker: EventEmitter?
136
+ get() = legacyModule()
137
+
138
+ internal val errorManager: ErrorManagerModule?
139
+ get() = registry.getModule()
140
+
131
141
  fun onDestroy() {
132
142
  reactContextHolder.get()?.removeLifecycleEventListener(reactLifecycleDelegate)
133
143
  registry.post(EventName.MODULE_DESTROY)
@@ -3,7 +3,9 @@ package expo.modules.kotlin
3
3
  import com.facebook.react.bridge.Dynamic
4
4
 
5
5
  inline fun <T> Dynamic.recycle(block: Dynamic.() -> T): T {
6
- val result = block(this)
7
- this.recycle()
8
- return result
6
+ try {
7
+ return block(this)
8
+ } finally {
9
+ this.recycle()
10
+ }
9
11
  }
@@ -2,17 +2,23 @@ package expo.modules.kotlin
2
2
 
3
3
  import android.os.Bundle
4
4
  import com.facebook.react.bridge.Arguments
5
+ import expo.modules.kotlin.records.Record
6
+ import expo.modules.kotlin.records.toJSMap
5
7
 
6
8
  class KPromiseWrapper(
7
9
  private val bridgePromise: com.facebook.react.bridge.Promise
8
10
  ) : Promise {
9
11
 
12
+ @Suppress("UNCHECKED_CAST")
10
13
  override fun resolve(value: Any?) {
11
14
  bridgePromise.resolve(
12
15
  when (value) {
13
16
  is Unit -> null
14
- is Bundle -> Arguments.fromBundle(value as Bundle?)
15
- is List<*> -> Arguments.fromList(value as List<*>?)
17
+ is Bundle -> Arguments.fromBundle(value)
18
+ is List<*> -> Arguments.fromList(value)
19
+ is Array<*> -> Arguments.fromArray(value)
20
+ is Map<*, *> -> Arguments.makeNativeMap(value as Map<String, Any?>) // TODO(@lukmccall): add more sophisticated conversion method
21
+ is Record -> value.toJSMap()
16
22
  else -> value
17
23
  }
18
24
  )
@@ -3,6 +3,8 @@ package expo.modules.kotlin
3
3
  import com.facebook.react.bridge.ReactApplicationContext
4
4
  import com.facebook.react.bridge.ReadableArray
5
5
  import com.facebook.react.uimanager.ViewManager
6
+ import expo.modules.kotlin.exception.CodedException
7
+ import expo.modules.kotlin.exception.UnexpectedException
6
8
  import expo.modules.kotlin.views.GroupViewManagerWrapper
7
9
  import expo.modules.kotlin.views.SimpleViewManagerWrapper
8
10
  import expo.modules.kotlin.views.ViewManagerWrapperDelegate
@@ -18,7 +20,7 @@ class KotlinInteropModuleRegistry(
18
20
  legacyModuleRegistry: expo.modules.core.ModuleRegistry,
19
21
  reactContext: WeakReference<ReactApplicationContext>
20
22
  ) {
21
- private val appContext = AppContext(modulesProvider, legacyModuleRegistry, reactContext)
23
+ internal val appContext = AppContext(modulesProvider, legacyModuleRegistry, reactContext)
22
24
 
23
25
  private val registry: ModuleRegistry
24
26
  get() = appContext.registry
@@ -26,9 +28,16 @@ class KotlinInteropModuleRegistry(
26
28
  fun hasModule(name: String): Boolean = registry.hasModule(name)
27
29
 
28
30
  fun callMethod(moduleName: String, method: String, arguments: ReadableArray, promise: Promise) {
29
- registry
30
- .getModuleHolder(moduleName)
31
- ?.call(method, arguments, promise)
31
+ try {
32
+ requireNotNull(
33
+ registry.getModuleHolder(moduleName)
34
+ ) { "Trying to call '$method' on the non-existing module '$moduleName'" }
35
+ .call(method, arguments, promise)
36
+ } catch (e: CodedException) {
37
+ promise.reject(e)
38
+ } catch (e: Throwable) {
39
+ promise.reject(UnexpectedException(e))
40
+ }
32
41
  }
33
42
 
34
43
  fun exportedModulesConstants(): Map<ModuleName, ModuleConstants> {
@@ -1,23 +1,24 @@
1
1
  package expo.modules.kotlin
2
2
 
3
3
  import com.facebook.react.bridge.ReadableArray
4
- import expo.modules.core.utilities.ifNull
5
4
  import expo.modules.kotlin.events.BasicEventListener
6
5
  import expo.modules.kotlin.events.EventListenerWithPayload
7
6
  import expo.modules.kotlin.events.EventListenerWithSenderAndPayload
8
7
  import expo.modules.kotlin.events.EventName
8
+ import expo.modules.kotlin.exception.FunctionCallException
9
9
  import expo.modules.kotlin.exception.MethodNotFoundException
10
+ import expo.modules.kotlin.exception.exceptionDecorator
10
11
  import expo.modules.kotlin.modules.Module
11
12
 
12
13
  class ModuleHolder(val module: Module) {
13
14
  val definition = module.definition()
14
15
  val name get() = definition.name
15
16
 
16
- fun call(methodName: String, args: ReadableArray, promise: Promise) {
17
- val method = definition.methods[methodName].ifNull {
18
- promise.reject(MethodNotFoundException(methodName, definition.name))
19
- return
20
- }
17
+ fun call(methodName: String, args: ReadableArray, promise: Promise) = exceptionDecorator({
18
+ FunctionCallException(methodName, definition.name, it)
19
+ }) {
20
+ val method = definition.methods[methodName]
21
+ ?: throw MethodNotFoundException()
21
22
 
22
23
  method.call(args, promise)
23
24
  }
@@ -7,7 +7,8 @@ import java.lang.ref.WeakReference
7
7
  class ModuleRegistry(
8
8
  private val appContext: WeakReference<AppContext>
9
9
  ) : Iterable<ModuleHolder> {
10
- private val registry = mutableMapOf<String, ModuleHolder>()
10
+ @PublishedApi
11
+ internal val registry = mutableMapOf<String, ModuleHolder>()
11
12
 
12
13
  fun register(module: Module) {
13
14
  val holder = ModuleHolder(module)
@@ -27,6 +28,10 @@ class ModuleRegistry(
27
28
 
28
29
  fun getModule(name: String): Module? = registry[name]?.module
29
30
 
31
+ inline fun <reified T> getModule(): T? {
32
+ return registry.values.find { it.module is T }?.module as? T
33
+ }
34
+
30
35
  fun getModuleHolder(name: String): ModuleHolder? = registry[name]
31
36
 
32
37
  fun getModuleHolder(module: Module): ModuleHolder? =
@@ -8,6 +8,6 @@ interface Promise {
8
8
  fun reject(code: String, message: String?, cause: Throwable?)
9
9
 
10
10
  fun reject(exception: CodedException) {
11
- reject(exception.code, exception.message, exception.cause)
11
+ reject(exception.code, exception.localizedMessage, exception.cause)
12
12
  }
13
13
  }
@@ -0,0 +1,5 @@
1
+ package expo.modules.kotlin.callbacks
2
+
3
+ fun interface Callback<T> {
4
+ operator fun invoke(arg: T)
5
+ }
@@ -0,0 +1,28 @@
1
+ package expo.modules.kotlin.callbacks
2
+
3
+ import android.os.Bundle
4
+ import android.view.View
5
+ import com.facebook.react.bridge.ReactContext
6
+ import expo.modules.adapters.react.NativeModulesProxy
7
+ import expo.modules.kotlin.modules.Module
8
+ import kotlin.reflect.KType
9
+
10
+ class ViewCallback<T>(
11
+ private val name: String,
12
+ private val type: KType,
13
+ private val view: View
14
+ ) : Callback<T> {
15
+ internal lateinit var module: Module
16
+
17
+ override operator fun invoke(arg: T) {
18
+ val reactContext = view.context as ReactContext
19
+ val nativeModulesProxy = reactContext
20
+ .catalystInstance
21
+ ?.getNativeModule("NativeUnimoduleProxy") as? NativeModulesProxy
22
+ ?: return
23
+ val appContext = nativeModulesProxy.kotlinInteropModuleRegistry.appContext
24
+
25
+ // TODO(@lukmccall): handles other types
26
+ appContext.callbackInvoker?.emit(view.id, name, arg as Bundle)
27
+ }
28
+ }
@@ -0,0 +1,27 @@
1
+ @file:OptIn(ExperimentalStdlibApi::class)
2
+
3
+ package expo.modules.kotlin.callbacks
4
+
5
+ import android.view.View
6
+ import java.lang.ref.WeakReference
7
+ import kotlin.reflect.KProperty
8
+ import kotlin.reflect.KType
9
+ import kotlin.reflect.typeOf
10
+
11
+ class ViewCallbackDelegate<T>(private val type: KType, view: View) {
12
+ private val viewHolder = WeakReference(view)
13
+ internal var isValidated = false
14
+
15
+ operator fun getValue(thisRef: View, property: KProperty<*>): Callback<T> {
16
+ if (!isValidated) {
17
+ throw IllegalStateException("You have to export this property as a callback in the `ViewManager`.")
18
+ }
19
+
20
+ val view = viewHolder.get() ?: throw IllegalStateException("Can't send an event from the view that is deallocated.")
21
+ return ViewCallback(property.name, type, view)
22
+ }
23
+ }
24
+
25
+ inline fun <reified T> View.callback(): ViewCallbackDelegate<T> {
26
+ return ViewCallbackDelegate(typeOf<T>(), this)
27
+ }
@@ -0,0 +1,25 @@
1
+ package expo.modules.kotlin.defaultmodules
2
+
3
+ import android.os.Bundle
4
+ import expo.modules.kotlin.exception.CodedException
5
+ import expo.modules.kotlin.modules.Module
6
+ import expo.modules.kotlin.modules.ModuleDefinition
7
+
8
+ private const val onNewException = "ExpoModulesCoreErrorManager.onNewException"
9
+
10
+ class ErrorManagerModule : Module() {
11
+ override fun definition() = ModuleDefinition {
12
+ name("ExpoModulesCoreErrorManager")
13
+ events(onNewException)
14
+ }
15
+
16
+ fun reportExceptionToLogBox(codedException: CodedException) {
17
+ val eventEmitter = appContext.eventEmitter(this) ?: return
18
+ eventEmitter.emit(
19
+ onNewException,
20
+ Bundle().apply {
21
+ putString("message", codedException.message ?: codedException.toString())
22
+ }
23
+ )
24
+ }
25
+ }
@@ -1,5 +1,6 @@
1
1
  package expo.modules.kotlin.exception
2
2
 
3
+ import com.facebook.react.bridge.ReadableType
3
4
  import java.util.*
4
5
  import kotlin.reflect.KType
5
6
 
@@ -17,7 +18,7 @@ open class CodedException(
17
18
  val code
18
19
  get() = providedCode ?: inferCode(javaClass)
19
20
 
20
- constructor(code: String, message: String, cause: Throwable?) : this(message, cause) {
21
+ constructor(code: String, message: String?, cause: Throwable?) : this(message, cause) {
21
22
  providedCode = code
22
23
  }
23
24
 
@@ -47,24 +48,83 @@ internal class IncompatibleArgTypeException(
47
48
  desiredType: KType,
48
49
  cause: Throwable? = null
49
50
  ) : CodedException(
50
- message = "Argument type $argumentType is not compatible with expected type $desiredType.",
51
+ message = "Argument type '$argumentType' is not compatible with expected type '$desiredType'.",
51
52
  cause = cause
52
53
  )
53
54
 
54
55
  internal class MissingTypeConverter(
55
56
  forType: KType
56
57
  ) : CodedException(
57
- message = "Cannot find type converter for $forType.",
58
+ message = "Cannot find type converter for '$forType'.",
58
59
  )
59
60
 
60
61
  internal class InvalidArgsNumberException(received: Int, expected: Int) :
61
62
  CodedException(message = "Received $received arguments, but $expected was expected.")
62
63
 
63
- internal class MethodNotFoundException(methodName: String, moduleName: String) :
64
- CodedException(message = "Cannot fund method $methodName in module $moduleName")
64
+ internal class MethodNotFoundException :
65
+ CodedException(message = "Method does not exist.")
65
66
 
66
- internal class NullArgumentException(desiredType: KType) :
67
- CodedException(message = "Cannot assigned null to not nullable type $desiredType")
67
+ internal class NullArgumentException :
68
+ CodedException(message = "Cannot assigned null to not nullable type.")
68
69
 
69
70
  internal class UnexpectedException(val throwable: Throwable) :
70
- CodedException(throwable)
71
+ CodedException(message = throwable.toString(), throwable)
72
+
73
+ /**
74
+ * A base class for all exceptions used in `exceptionDecorator` function.
75
+ */
76
+ internal open class DecoratedException(
77
+ message: String,
78
+ cause: CodedException,
79
+ ) : CodedException(
80
+ cause.code,
81
+ message = "$message${System.lineSeparator()}→ Caused by: ${cause.localizedMessage ?: cause}",
82
+ cause
83
+ )
84
+
85
+ internal class FunctionCallException(
86
+ methodName: String,
87
+ moduleName: String,
88
+ cause: CodedException
89
+ ) : DecoratedException(
90
+ message = "Call to function '$moduleName.$methodName' has been rejected.",
91
+ cause,
92
+ )
93
+
94
+ internal class ArgumentCastException(
95
+ argDesiredType: KType,
96
+ argIndex: Int,
97
+ providedType: ReadableType,
98
+ cause: CodedException,
99
+ ) : DecoratedException(
100
+ message = "Argument at index '$argIndex' couldn't be casted to type '$argDesiredType' (received '$providedType').",
101
+ cause,
102
+ )
103
+
104
+ internal class FieldCastException(
105
+ fieldName: String,
106
+ fieldType: KType,
107
+ providedType: ReadableType,
108
+ cause: CodedException
109
+ ) : DecoratedException(
110
+ message = "Cannot cast '${providedType.name}' for field '$fieldName' ('$fieldType').",
111
+ cause
112
+ )
113
+
114
+ internal class RecordCastException(
115
+ recordType: KType,
116
+ cause: CodedException
117
+ ) : DecoratedException(
118
+ message = "Cannot create a record of the type: '$recordType'.",
119
+ cause
120
+ )
121
+
122
+ internal class CollectionElementCastException(
123
+ collectionType: KType,
124
+ elementType: KType,
125
+ providedType: ReadableType,
126
+ cause: CodedException
127
+ ) : DecoratedException(
128
+ message = "Cannot cast '${providedType.name}' to '$elementType' required by the collection of type: '$collectionType'.",
129
+ cause
130
+ )
@@ -0,0 +1,11 @@
1
+ package expo.modules.kotlin.exception
2
+
3
+ internal inline fun <T> exceptionDecorator(decoratorBlock: (e: CodedException) -> Throwable, block: () -> T): T {
4
+ return try {
5
+ block()
6
+ } catch (e: CodedException) {
7
+ throw decoratorBlock(e)
8
+ } catch (e: Throwable) {
9
+ throw decoratorBlock(UnexpectedException(e))
10
+ }
11
+ }
@@ -2,31 +2,26 @@ package expo.modules.kotlin.methods
2
2
 
3
3
  import com.facebook.react.bridge.ReadableArray
4
4
  import expo.modules.kotlin.Promise
5
+ import expo.modules.kotlin.exception.ArgumentCastException
5
6
  import expo.modules.kotlin.exception.CodedException
6
7
  import expo.modules.kotlin.exception.InvalidArgsNumberException
7
- import expo.modules.kotlin.exception.UnexpectedException
8
+ import expo.modules.kotlin.exception.exceptionDecorator
8
9
  import expo.modules.kotlin.iterator
9
10
  import expo.modules.kotlin.recycle
10
11
  import expo.modules.kotlin.types.AnyType
11
- import kotlin.jvm.Throws
12
12
 
13
13
  abstract class AnyMethod(
14
14
  protected val name: String,
15
15
  private val desiredArgsTypes: Array<AnyType>
16
16
  ) {
17
+ @Throws(CodedException::class)
17
18
  fun call(args: ReadableArray, promise: Promise) {
18
19
  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))
20
+ throw InvalidArgsNumberException(args.size(), desiredArgsTypes.size)
29
21
  }
22
+
23
+ val convertedArgs = convertArgs(args)
24
+ callImplementation(convertedArgs, promise)
30
25
  }
31
26
 
32
27
  @Throws(CodedException::class)
@@ -40,9 +35,13 @@ abstract class AnyMethod(
40
35
  val argIterator = args.iterator()
41
36
  desiredArgsTypes
42
37
  .withIndex()
43
- .forEach { (index, type) ->
38
+ .forEach { (index, desiredType) ->
44
39
  argIterator.next().recycle {
45
- finalArgs[index] = type.convert(this)
40
+ exceptionDecorator({ cause ->
41
+ ArgumentCastException(desiredType.kType, index, type, cause)
42
+ }) {
43
+ finalArgs[index] = desiredType.convert(this)
44
+ }
46
45
  }
47
46
  }
48
47
  return finalArgs
@@ -0,0 +1,4 @@
1
+ package expo.modules.kotlin.modules
2
+
3
+ @DslMarker
4
+ internal annotation class DefinitionMarker
@@ -4,6 +4,7 @@ import android.os.Bundle
4
4
  import expo.modules.kotlin.AppContext
5
5
 
6
6
  abstract class Module {
7
+ @Suppress("PropertyName")
7
8
  internal var _appContext: AppContext? = null
8
9
 
9
10
  private val moduleEventEmitter by lazy { appContext.eventEmitter(this) }
@@ -19,6 +20,6 @@ abstract class Module {
19
20
  }
20
21
 
21
22
  @Suppress("FunctionName")
22
- inline fun ModuleDefinition(block: ModuleDefinitionBuilder.() -> Unit): ModuleDefinitionData {
23
- return ModuleDefinitionBuilder().also(block).build()
23
+ inline fun Module.ModuleDefinition(block: ModuleDefinitionBuilder.() -> Unit): ModuleDefinitionData {
24
+ return ModuleDefinitionBuilder(this).also(block).build()
24
25
  }
@@ -31,7 +31,8 @@ import expo.modules.kotlin.views.ViewManagerDefinition
31
31
  import expo.modules.kotlin.views.ViewManagerDefinitionBuilder
32
32
  import kotlin.reflect.typeOf
33
33
 
34
- class ModuleDefinitionBuilder {
34
+ @DefinitionMarker
35
+ class ModuleDefinitionBuilder(private val module: Module? = null) {
35
36
  private var name: String? = null
36
37
  private var constantsProvider = { emptyMap<String, Any?>() }
37
38
  private var eventsDefinition: EventsDefinition? = null
@@ -46,8 +47,10 @@ class ModuleDefinitionBuilder {
46
47
  internal val eventListeners = mutableMapOf<EventName, EventListener>()
47
48
 
48
49
  fun build(): ModuleDefinitionData {
50
+ val moduleName = name ?: module?.javaClass?.simpleName
51
+
49
52
  return ModuleDefinitionData(
50
- requireNotNull(name),
53
+ requireNotNull(moduleName),
51
54
  constantsProvider,
52
55
  methods,
53
56
  viewManagerDefinition,
@@ -56,14 +59,27 @@ class ModuleDefinitionBuilder {
56
59
  )
57
60
  }
58
61
 
62
+ /**
63
+ * Sets the name of the module that is exported to the JavaScript world.
64
+ */
59
65
  fun name(name: String) {
60
66
  this.name = name
61
67
  }
62
68
 
69
+ /**
70
+ * Definition function setting the module's constants to export.
71
+ */
63
72
  fun constants(constantsProvider: () -> Map<String, Any?>) {
64
73
  this.constantsProvider = constantsProvider
65
74
  }
66
75
 
76
+ /**
77
+ * Definition of the module's constants to export.
78
+ */
79
+ fun constants(vararg constants: Pair<String, Any?>) {
80
+ constantsProvider = { constants.toMap() }
81
+ }
82
+
67
83
  @JvmName("methodWithoutArgs")
68
84
  inline fun function(
69
85
  name: String,
@@ -167,6 +183,9 @@ class ModuleDefinitionBuilder {
167
183
  }
168
184
  }
169
185
 
186
+ /**
187
+ * Creates the view manager definition that scopes other view-related definitions.
188
+ */
170
189
  inline fun viewManager(body: ViewManagerDefinitionBuilder.() -> Unit) {
171
190
  require(viewManagerDefinition == null) { "The module definition may have exported only one view manager." }
172
191
 
@@ -175,22 +194,37 @@ class ModuleDefinitionBuilder {
175
194
  viewManagerDefinition = viewManagerDefinitionBuilder.build()
176
195
  }
177
196
 
197
+ /**
198
+ * Creates module's lifecycle listener that is called right after the module initialization.
199
+ */
178
200
  inline fun onCreate(crossinline body: () -> Unit) {
179
201
  eventListeners[EventName.MODULE_CREATE] = BasicEventListener(EventName.MODULE_CREATE) { body() }
180
202
  }
181
203
 
204
+ /**
205
+ * Creates module's lifecycle listener that is called when the module is about to be deallocated.
206
+ */
182
207
  inline fun onDestroy(crossinline body: () -> Unit) {
183
208
  eventListeners[EventName.MODULE_DESTROY] = BasicEventListener(EventName.MODULE_DESTROY) { body() }
184
209
  }
185
210
 
211
+ /**
212
+ * Creates module's lifecycle listener that is called right after the activity is resumed.
213
+ */
186
214
  inline fun onActivityEntersForeground(crossinline body: () -> Unit) {
187
215
  eventListeners[EventName.ACTIVITY_ENTERS_FOREGROUND] = BasicEventListener(EventName.ACTIVITY_ENTERS_FOREGROUND) { body() }
188
216
  }
189
217
 
218
+ /**
219
+ * Creates module's lifecycle listener that is called right after the activity is paused.
220
+ */
190
221
  inline fun onActivityEntersBackground(crossinline body: () -> Unit) {
191
222
  eventListeners[EventName.ACTIVITY_ENTERS_BACKGROUND] = BasicEventListener(EventName.ACTIVITY_ENTERS_BACKGROUND) { body() }
192
223
  }
193
224
 
225
+ /**
226
+ * Creates module's lifecycle listener that is called right after the activity is destroyed.
227
+ */
194
228
  inline fun onActivityDestroys(crossinline body: () -> Unit) {
195
229
  eventListeners[EventName.ACTIVITY_DESTROYS] = BasicEventListener(EventName.ACTIVITY_DESTROYS) { body() }
196
230
  }
@@ -203,23 +237,29 @@ class ModuleDefinitionBuilder {
203
237
  }
204
238
 
205
239
  /**
206
- * Method that is invoked when the first event listener is added.
240
+ * Creates module's lifecycle listener that is called right after the first event listener is added.
207
241
  */
208
242
  inline fun onStartObserving(crossinline body: () -> Unit) {
209
243
  function("startObserving", body)
210
244
  }
211
245
 
212
246
  /**
213
- * Method that is invoked when all event listeners are removed.
247
+ * Creates module's lifecycle listener that is called right after all event listeners are removed.
214
248
  */
215
249
  inline fun onStopObserving(crossinline body: () -> Unit) {
216
250
  function("stopObserving", body)
217
251
  }
218
252
 
253
+ /**
254
+ * Creates module's lifecycle listener that is called right after the new intent was received.
255
+ */
219
256
  inline fun onNewIntent(crossinline body: (Intent) -> Unit) {
220
257
  eventListeners[EventName.ON_NEW_INTENT] = EventListenerWithPayload<Intent>(EventName.ON_NEW_INTENT) { body(it) }
221
258
  }
222
259
 
260
+ /**
261
+ * Creates module's lifecycle listener that is called right after the activity has received a result.
262
+ */
223
263
  inline fun onActivityResult(crossinline body: (Activity, OnActivityResultPayload) -> Unit) {
224
264
  eventListeners[EventName.ON_ACTIVITY_RESULT] =
225
265
  EventListenerWithSenderAndPayload<Activity, OnActivityResultPayload>(EventName.ON_ACTIVITY_RESULT) { sender, payload -> body(sender, payload) }