expo-modules-core 0.6.1 → 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 (161) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +1 -1
  3. package/android/build.gradle +5 -5
  4. package/android/src/main/java/expo/modules/adapters/react/apploader/RNHeadlessAppLoader.kt +7 -1
  5. package/android/src/main/java/expo/modules/core/interfaces/ReactActivityLifecycleListener.java +23 -0
  6. package/android/src/main/java/expo/modules/kotlin/AppContext.kt +11 -1
  7. package/android/src/main/java/expo/modules/kotlin/DynamicExtenstions.kt +5 -3
  8. package/android/src/main/java/expo/modules/kotlin/KPromiseWrapper.kt +8 -2
  9. package/android/src/main/java/expo/modules/kotlin/KotlinInteropModuleRegistry.kt +13 -4
  10. package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +7 -6
  11. package/android/src/main/java/expo/modules/kotlin/ModuleRegistry.kt +6 -1
  12. package/android/src/main/java/expo/modules/kotlin/Promise.kt +1 -1
  13. package/android/src/main/java/expo/modules/kotlin/callbacks/Callback.kt +5 -0
  14. package/android/src/main/java/expo/modules/kotlin/callbacks/ViewCallback.kt +28 -0
  15. package/android/src/main/java/expo/modules/kotlin/callbacks/ViewCallbackDelegate.kt +27 -0
  16. package/android/src/main/java/expo/modules/kotlin/defaultmodules/ErrorManagerModule.kt +25 -0
  17. package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +68 -8
  18. package/android/src/main/java/expo/modules/kotlin/exception/ExceptionDecorator.kt +11 -0
  19. package/android/src/main/java/expo/modules/kotlin/methods/AnyMethod.kt +13 -14
  20. package/android/src/main/java/expo/modules/kotlin/modules/DefinitionMarker.kt +4 -0
  21. package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +3 -2
  22. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +44 -4
  23. package/android/src/main/java/expo/modules/kotlin/records/Record.kt +39 -0
  24. package/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt +14 -7
  25. package/android/src/main/java/expo/modules/kotlin/types/ArrayTypeConverter.kt +11 -5
  26. package/android/src/main/java/expo/modules/kotlin/types/ListTypeConverter.kt +10 -4
  27. package/android/src/main/java/expo/modules/kotlin/types/MapTypeConverter.kt +12 -6
  28. package/android/src/main/java/expo/modules/kotlin/types/PairTypeConverter.kt +29 -13
  29. package/android/src/main/java/expo/modules/kotlin/types/TypeConverter.kt +2 -1
  30. package/android/src/main/java/expo/modules/kotlin/views/CallbacksDefinition.kt +3 -0
  31. package/android/src/main/java/expo/modules/kotlin/views/GroupViewManagerWrapper.kt +22 -0
  32. package/android/src/main/java/expo/modules/kotlin/views/SimpleViewManagerWrapper.kt +22 -0
  33. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinition.kt +27 -2
  34. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinitionBuilder.kt +29 -1
  35. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerWrapperDelegate.kt +59 -2
  36. package/build/EventEmitter.d.ts +1 -0
  37. package/build/EventEmitter.d.ts.map +1 -0
  38. package/build/NativeModulesProxy.d.ts +1 -0
  39. package/build/NativeModulesProxy.d.ts.map +1 -0
  40. package/build/NativeModulesProxy.native.d.ts +1 -0
  41. package/build/NativeModulesProxy.native.d.ts.map +1 -0
  42. package/build/NativeModulesProxy.types.d.ts +1 -0
  43. package/build/NativeModulesProxy.types.d.ts.map +1 -0
  44. package/build/NativeViewManagerAdapter.d.ts +1 -0
  45. package/build/NativeViewManagerAdapter.d.ts.map +1 -0
  46. package/build/NativeViewManagerAdapter.native.d.ts +1 -0
  47. package/build/NativeViewManagerAdapter.native.d.ts.map +1 -0
  48. package/build/PermissionsHook.d.ts +1 -0
  49. package/build/PermissionsHook.d.ts.map +1 -0
  50. package/build/PermissionsInterface.d.ts +1 -0
  51. package/build/PermissionsInterface.d.ts.map +1 -0
  52. package/build/Platform.d.ts +1 -0
  53. package/build/Platform.d.ts.map +1 -0
  54. package/build/SyntheticPlatformEmitter.d.ts +1 -0
  55. package/build/SyntheticPlatformEmitter.d.ts.map +1 -0
  56. package/build/SyntheticPlatformEmitter.web.d.ts +1 -0
  57. package/build/SyntheticPlatformEmitter.web.d.ts.map +1 -0
  58. package/build/deprecate.d.ts +1 -0
  59. package/build/deprecate.d.ts.map +1 -0
  60. package/build/environment/browser.d.ts +1 -0
  61. package/build/environment/browser.d.ts.map +1 -0
  62. package/build/environment/browser.web.d.ts +1 -0
  63. package/build/environment/browser.web.d.ts.map +1 -0
  64. package/build/errors/CodedError.d.ts +1 -0
  65. package/build/errors/CodedError.d.ts.map +1 -0
  66. package/build/errors/UnavailabilityError.d.ts +1 -0
  67. package/build/errors/UnavailabilityError.d.ts.map +1 -0
  68. package/build/index.d.ts +3 -0
  69. package/build/index.d.ts.map +1 -0
  70. package/build/index.js +2 -0
  71. package/build/index.js.map +1 -1
  72. package/build/requireNativeModule.d.ts +16 -0
  73. package/build/requireNativeModule.d.ts.map +1 -0
  74. package/build/requireNativeModule.js +18 -0
  75. package/build/requireNativeModule.js.map +1 -0
  76. package/build/sweet/NativeErrorManager.d.ts +3 -0
  77. package/build/sweet/NativeErrorManager.d.ts.map +1 -0
  78. package/build/sweet/NativeErrorManager.js +3 -0
  79. package/build/sweet/NativeErrorManager.js.map +1 -0
  80. package/build/sweet/setUpErrorManager.fx.d.ts +2 -0
  81. package/build/sweet/setUpErrorManager.fx.d.ts.map +1 -0
  82. package/build/sweet/setUpErrorManager.fx.js +11 -0
  83. package/build/sweet/setUpErrorManager.fx.js.map +1 -0
  84. package/ios/AppDelegates/ExpoAppDelegate.swift +27 -8
  85. package/ios/JSI/ExpoModulesHostObject.h +33 -0
  86. package/ios/JSI/ExpoModulesHostObject.mm +40 -0
  87. package/ios/JSI/ExpoModulesProxySpec.h +4 -0
  88. package/ios/JSI/ExpoModulesProxySpec.mm +1 -3
  89. package/ios/JSI/JSIConversions.h +2 -0
  90. package/ios/JSI/JSIConversions.mm +9 -0
  91. package/ios/JSI/JSIInstaller.h +10 -0
  92. package/ios/JSI/JSIInstaller.mm +14 -2
  93. package/ios/JSI/JavaScriptObject.h +60 -0
  94. package/ios/JSI/JavaScriptObject.mm +93 -0
  95. package/ios/JSI/JavaScriptRuntime.h +54 -0
  96. package/ios/JSI/JavaScriptRuntime.mm +102 -0
  97. package/ios/ModuleRegistryAdapter/EXModuleRegistryAdapter.m +2 -12
  98. package/ios/NativeModulesProxy/EXComponentDataCompatibleWrapper.h +16 -0
  99. package/ios/NativeModulesProxy/EXComponentDataCompatibleWrapper.m +28 -0
  100. package/ios/NativeModulesProxy/EXNativeModulesProxy.mm +90 -66
  101. package/ios/RCTComponentData+Privates.h +12 -0
  102. package/ios/ReactDelegates/ExpoReactDelegate.swift +2 -2
  103. package/ios/ReactDelegates/ExpoReactDelegateHandler.swift +3 -3
  104. package/ios/ReactDelegates/ModulePriorities.swift +1 -1
  105. package/ios/Swift/AppContext.swift +38 -4
  106. package/ios/Swift/Arguments/ArgumentType.swift +4 -0
  107. package/ios/Swift/Arguments/Convertibles.swift +13 -13
  108. package/ios/Swift/Arguments/Types/EnumArgumentType.swift +11 -17
  109. package/ios/Swift/Arguments/Types/PromiseArgumentType.swift +1 -1
  110. package/ios/Swift/Arguments/Types/RawArgumentType.swift +2 -2
  111. package/ios/Swift/Conversions.swift +51 -56
  112. package/ios/Swift/EventListener.swift +8 -10
  113. package/ios/Swift/Events/Callback.swift +66 -0
  114. package/ios/Swift/Events/Event.swift +43 -0
  115. package/ios/Swift/Exceptions/ChainableException.swift +51 -0
  116. package/ios/Swift/{CodedError.swift → Exceptions/CodedError.swift} +1 -12
  117. package/ios/Swift/Exceptions/Exception.swift +62 -0
  118. package/ios/Swift/Exceptions/ExceptionOrigin.swift +28 -0
  119. package/ios/Swift/Exceptions/GenericException.swift +20 -0
  120. package/ios/Swift/Exceptions/UnexpectedException.swift +16 -0
  121. package/ios/Swift/Functions/AnyFunction.swift +11 -1
  122. package/ios/Swift/Functions/ConcreteFunction.swift +37 -16
  123. package/ios/Swift/JavaScriptUtils.swift +43 -0
  124. package/ios/Swift/ModuleHolder.swift +53 -14
  125. package/ios/Swift/ModuleRegistry.swift +4 -1
  126. package/ios/Swift/Modules/AnyModule.swift +0 -1
  127. package/ios/Swift/Modules/ModuleDefinition.swift +4 -13
  128. package/ios/Swift/Modules/ModuleDefinitionBuilder.swift +0 -1
  129. package/ios/Swift/Modules/ModuleDefinitionComponents.swift +0 -188
  130. package/ios/Swift/ModulesProvider.swift +0 -1
  131. package/ios/Swift/Objects/ObjectDefinition.swift +30 -0
  132. package/ios/Swift/Objects/ObjectDefinitionComponents.swift +208 -0
  133. package/ios/Swift/Promise.swift +8 -3
  134. package/ios/Swift/Records/AnyField.swift +7 -0
  135. package/ios/Swift/Records/Field.swift +24 -19
  136. package/ios/Swift/Records/FieldOption.swift +1 -1
  137. package/ios/Swift/Records/Record.swift +12 -4
  138. package/ios/Swift/SwiftInteropBridge.swift +39 -10
  139. package/ios/Swift/Views/AnyViewProp.swift +1 -1
  140. package/ios/Swift/Views/ComponentData.swift +95 -0
  141. package/ios/Swift/Views/ConcreteViewProp.swift +6 -8
  142. package/ios/Swift/Views/ViewFactory.swift +1 -1
  143. package/ios/Swift/Views/ViewManagerDefinition.swift +23 -2
  144. package/ios/Swift/Views/ViewManagerDefinitionBuilder.swift +0 -1
  145. package/ios/Swift/Views/ViewManagerDefinitionComponents.swift +26 -0
  146. package/ios/Swift/Views/ViewModuleWrapper.swift +5 -2
  147. package/ios/Tests/ArgumentTypeSpec.swift +3 -4
  148. package/ios/Tests/ConstantsSpec.swift +4 -4
  149. package/ios/Tests/ConvertiblesSpec.swift +33 -33
  150. package/ios/Tests/ExceptionsSpec.swift +112 -0
  151. package/ios/Tests/FunctionSpec.swift +20 -22
  152. package/ios/Tests/FunctionWithConvertiblesSpec.swift +2 -2
  153. package/ios/Tests/Mocks/ModuleMocks.swift +1 -1
  154. package/ios/Tests/Mocks/ModulesProviderMock.swift +0 -1
  155. package/ios/Tests/ModuleEventListenersSpec.swift +1 -1
  156. package/ios/Tests/RecordSpec.swift +7 -17
  157. package/package.json +3 -3
  158. package/src/index.ts +4 -0
  159. package/src/requireNativeModule.ts +29 -0
  160. package/src/sweet/NativeErrorManager.ts +2 -0
  161. package/src/sweet/setUpErrorManager.fx.ts +12 -0
package/CHANGELOG.md CHANGED
@@ -10,6 +10,44 @@
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
+
33
+ ## 0.6.4 — 2022-01-05
34
+
35
+ ### 🐛 Bug fixes
36
+
37
+ - Fix `ReactInstanceManager.onHostPause` exception from moving Android apps to background. ([#15748](https://github.com/expo/expo/pull/15748) by [@kudo](https://github.com/kudo))
38
+
39
+ ## 0.6.3 — 2021-12-16
40
+
41
+ ### 🐛 Bug fixes
42
+
43
+ - Fixed the deep link wasn't passed to the application if the application wasn't running when the deep link was sent. ([#15593](https://github.com/expo/expo/pull/15593) by [@lukmccall](https://github.com/lukmccall))
44
+
45
+ ## 0.6.2 — 2021-12-15
46
+
47
+ ### 🎉 New features
48
+
49
+ - Add `onNewIntent` and `onBackPressed` support to `ReactActivityLifecycleListener`. ([#15550](https://github.com/expo/expo/pull/15550) by [@Kudo](https://github.com/Kudo))
50
+
13
51
  ## 0.6.1 — 2021-12-08
14
52
 
15
53
  _This version does not introduce any user-facing changes._
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.1'
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.1"
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
@@ -3,6 +3,7 @@ package expo.modules.adapters.react.apploader
3
3
  import android.content.Context
4
4
  import com.facebook.react.ReactApplication
5
5
  import com.facebook.react.ReactInstanceManager
6
+ import com.facebook.react.common.LifecycleState
6
7
  import expo.modules.apploader.HeadlessAppLoader
7
8
  import expo.modules.core.interfaces.Consumer
8
9
  import expo.modules.core.interfaces.DoNotStrip
@@ -43,7 +44,12 @@ class RNHeadlessAppLoader @DoNotStrip constructor(private val context: Context)
43
44
  return if (appRecords.containsKey(appScopeKey) && appRecords[appScopeKey] != null) {
44
45
  val appRecord: ReactInstanceManager = appRecords[appScopeKey]!!
45
46
  android.os.Handler(context.mainLooper).post {
46
- appRecord.destroy()
47
+ // Only destroy the `ReactInstanceManager` if it does not bind with an Activity.
48
+ // And The Activity would take over the ownership of `ReactInstanceManager`.
49
+ // This case happens when a user clicks a background task triggered notification immediately.
50
+ if (appRecord.lifecycleState == LifecycleState.BEFORE_CREATE) {
51
+ appRecord.destroy()
52
+ }
47
53
  HeadlessAppLoaderNotifier.notifyAppDestroyed(appScopeKey)
48
54
  appRecords.remove(appScopeKey)
49
55
  }
@@ -1,6 +1,7 @@
1
1
  package expo.modules.core.interfaces;
2
2
 
3
3
  import android.app.Activity;
4
+ import android.content.Intent;
4
5
  import android.os.Bundle;
5
6
 
6
7
  public interface ReactActivityLifecycleListener {
@@ -11,4 +12,26 @@ public interface ReactActivityLifecycleListener {
11
12
  default void onPause(Activity activity) {}
12
13
 
13
14
  default void onDestroy(Activity activity) {}
15
+
16
+ /**
17
+ * Called when {@link com.facebook.react.ReactActivity} received `onNewIntent`
18
+ * Every listener will receive this callback.
19
+ * `ReactActivityDelegateWrapper.onNewIntent` will get `true` if there's some module returns `true`
20
+ *
21
+ * @return true if this module wants to return `true` from `ReactActivityDelegateWrapper.onNewIntent`
22
+ */
23
+ default boolean onNewIntent(Intent intent) {
24
+ return false;
25
+ }
26
+
27
+ /**
28
+ * Called when {@link com.facebook.react.ReactActivity} received `onBackPressed`
29
+ * Every listener will receive this callback.
30
+ * `ReactActivityDelegateWrapper.onBackPressed` will get `true` if there's some module returns `true`
31
+ *
32
+ * @return true if this module wants to return `true` from `ReactActivityDelegateWrapper.onBackPressed`
33
+ */
34
+ default boolean onBackPressed() {
35
+ return false;
36
+ }
14
37
  }
@@ -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
  }