expo-modules-core 1.2.6 → 1.3.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 (180) hide show
  1. package/CHANGELOG.md +33 -4
  2. package/ExpoModulesCore.podspec +1 -0
  3. package/README.md +1 -1
  4. package/android/ExpoModulesCorePlugin.gradle +16 -0
  5. package/android/build.gradle +3 -2
  6. package/android/src/main/cpp/Exceptions.cpp +8 -0
  7. package/android/src/main/cpp/Exceptions.h +11 -0
  8. package/android/src/main/cpp/ExpoModulesHostObject.cpp +22 -5
  9. package/android/src/main/cpp/ExpoModulesHostObject.h +5 -0
  10. package/android/src/main/cpp/JNIInjector.cpp +2 -0
  11. package/android/src/main/cpp/JSIInteropModuleRegistry.cpp +25 -1
  12. package/android/src/main/cpp/JSIInteropModuleRegistry.h +14 -0
  13. package/android/src/main/cpp/JSIObjectWrapper.h +15 -4
  14. package/android/src/main/cpp/JSITypeConverter.h +3 -2
  15. package/android/src/main/cpp/JavaReferencesCache.cpp +2 -0
  16. package/android/src/main/cpp/JavaScriptFunction.cpp +56 -0
  17. package/android/src/main/cpp/JavaScriptFunction.h +54 -0
  18. package/android/src/main/cpp/JavaScriptModuleObject.cpp +225 -105
  19. package/android/src/main/cpp/JavaScriptModuleObject.h +67 -34
  20. package/android/src/main/cpp/JavaScriptObject.cpp +55 -1
  21. package/android/src/main/cpp/JavaScriptObject.h +17 -13
  22. package/android/src/main/cpp/JavaScriptRuntime.cpp +12 -3
  23. package/android/src/main/cpp/JavaScriptRuntime.h +9 -1
  24. package/android/src/main/cpp/JavaScriptValue.cpp +9 -0
  25. package/android/src/main/cpp/JavaScriptValue.h +4 -0
  26. package/android/src/main/cpp/MethodMetadata.cpp +66 -87
  27. package/android/src/main/cpp/MethodMetadata.h +18 -16
  28. package/android/src/main/cpp/ObjectDeallocator.h +25 -0
  29. package/android/src/main/cpp/WeakRuntimeHolder.cpp +7 -0
  30. package/android/src/main/cpp/WeakRuntimeHolder.h +4 -0
  31. package/android/src/main/cpp/types/CppType.h +4 -1
  32. package/android/src/main/cpp/types/FrontendConverter.cpp +58 -0
  33. package/android/src/main/cpp/types/FrontendConverter.h +45 -0
  34. package/android/src/main/cpp/types/FrontendConverterProvider.cpp +3 -0
  35. package/android/src/main/cpp/types/JNIToJSIConverter.cpp +88 -0
  36. package/android/src/main/cpp/types/JNIToJSIConverter.h +22 -0
  37. package/android/src/main/java/com/facebook/react/uimanager/ReactStylesDiffMapHelper.kt +10 -0
  38. package/android/src/main/java/expo/modules/kotlin/AppContext.kt +18 -25
  39. package/android/src/main/java/expo/modules/kotlin/FilteredIterator.kt +37 -0
  40. package/android/src/main/java/expo/modules/kotlin/KotlinInteropModuleRegistry.kt +1 -1
  41. package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +34 -21
  42. package/android/src/main/java/expo/modules/kotlin/ModuleRegistry.kt +13 -3
  43. package/android/src/main/java/expo/modules/kotlin/Utils.kt +21 -0
  44. package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultCaller.kt +21 -1
  45. package/android/src/main/java/expo/modules/kotlin/classcomponent/ClassComponentBuilder.kt +112 -0
  46. package/android/src/main/java/expo/modules/kotlin/classcomponent/ClassDefinitionData.kt +10 -0
  47. package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +21 -0
  48. package/android/src/main/java/expo/modules/kotlin/exception/CommonExceptions.kt +15 -0
  49. package/android/src/main/java/expo/modules/kotlin/functions/AnyFunction.kt +17 -4
  50. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunction.kt +38 -8
  51. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionComponent.kt +3 -2
  52. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionWithPromiseComponent.kt +3 -2
  53. package/android/src/main/java/expo/modules/kotlin/functions/SuspendFunctionComponent.kt +1 -0
  54. package/android/src/main/java/expo/modules/kotlin/functions/SyncFunctionComponent.kt +18 -11
  55. package/android/src/main/java/expo/modules/kotlin/jni/CppType.kt +4 -1
  56. package/android/src/main/java/expo/modules/kotlin/jni/JNIDeallocator.kt +73 -0
  57. package/android/src/main/java/expo/modules/kotlin/jni/JSIInteropModuleRegistry.kt +28 -2
  58. package/android/src/main/java/expo/modules/kotlin/jni/JavaCallback.kt +8 -1
  59. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptFunction.kt +48 -0
  60. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptModuleObject.kt +40 -3
  61. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptObject.kt +23 -3
  62. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptValue.kt +26 -1
  63. package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +0 -11
  64. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +26 -16
  65. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionData.kt +3 -1
  66. package/android/src/main/java/expo/modules/kotlin/sharedobjects/SharedObject.kt +12 -0
  67. package/android/src/main/java/expo/modules/kotlin/sharedobjects/SharedObjectRegistry.kt +62 -0
  68. package/android/src/main/java/expo/modules/kotlin/sharedobjects/SharedObjectTypeConverter.kt +27 -0
  69. package/android/src/main/java/expo/modules/kotlin/types/AnyType.kt +2 -1
  70. package/android/src/main/java/expo/modules/kotlin/types/EitherTypeConverter.kt +7 -6
  71. package/android/src/main/java/expo/modules/kotlin/types/JavaScriptFunctionTypeConverter.kt +22 -0
  72. package/android/src/main/java/expo/modules/kotlin/types/TypeConverter.kt +30 -24
  73. package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +45 -1
  74. package/android/src/main/java/expo/modules/kotlin/types/TypedArrayTypeConverter.kt +3 -2
  75. package/android/src/main/java/expo/modules/kotlin/views/AnyViewProp.kt +3 -1
  76. package/android/src/main/java/expo/modules/kotlin/views/ConcreteViewProp.kt +3 -3
  77. package/android/src/main/java/expo/modules/kotlin/views/FilteredReadableMap.kt +53 -0
  78. package/android/src/main/java/expo/modules/kotlin/views/GroupViewManagerWrapper.kt +25 -5
  79. package/android/src/main/java/expo/modules/kotlin/views/SimpleViewManagerWrapper.kt +25 -5
  80. package/android/src/main/java/expo/modules/kotlin/views/ViewDefinitionBuilder.kt +161 -10
  81. package/android/src/main/java/expo/modules/kotlin/views/ViewGroupDefinitionBuilder.kt +0 -67
  82. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinition.kt +7 -8
  83. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerWrapperDelegate.kt +40 -3
  84. package/android/src/main/java/expo/modules/kotlin/views/ViewTypeConverter.kt +44 -0
  85. package/android-annotation/build.gradle +45 -0
  86. package/android-annotation/src/main/java/expo/modules/annotation/Config.kt +7 -0
  87. package/android-annotation/src/main/java/expo/modules/annotation/ConverterBinder.kt +7 -0
  88. package/android-annotation-processor/build.gradle +51 -0
  89. package/android-annotation-processor/src/main/java/expo/modules/annotationprocessor/ExpoSymbolProcessor.kt +175 -0
  90. package/android-annotation-processor/src/main/java/expo/modules/annotationprocessor/ExpoSymbolProcessorProvider.kt +10 -0
  91. package/android-annotation-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider +1 -0
  92. package/build/NativeViewManagerAdapter.native.d.ts.map +1 -1
  93. package/build/NativeViewManagerAdapter.native.js +36 -23
  94. package/build/NativeViewManagerAdapter.native.js.map +1 -1
  95. package/build/requireNativeModule.js +2 -2
  96. package/build/requireNativeModule.js.map +1 -1
  97. package/common/cpp/fabric/ExpoViewProps.cpp +18 -3
  98. package/common/cpp/fabric/ExpoViewProps.h +4 -1
  99. package/ios/Fabric/ExpoFabricView.swift +10 -10
  100. package/ios/Fabric/ExpoFabricViewObjC.h +2 -0
  101. package/ios/Fabric/ExpoFabricViewObjC.mm +17 -2
  102. package/ios/JSI/EXJSIInstaller.mm +1 -1
  103. package/ios/JSI/EXJSIUtils.h +5 -0
  104. package/ios/JSI/EXJSIUtils.mm +17 -0
  105. package/ios/JSI/EXJavaScriptRuntime.h +5 -0
  106. package/ios/JSI/EXJavaScriptRuntime.mm +6 -0
  107. package/ios/JSI/EXJavaScriptValue.h +2 -0
  108. package/ios/JSI/EXJavaScriptValue.mm +8 -0
  109. package/ios/JSI/EXJavaScriptWeakObject.mm +29 -8
  110. package/ios/JSI/EXRawJavaScriptFunction.h +24 -0
  111. package/ios/JSI/EXRawJavaScriptFunction.mm +52 -0
  112. package/ios/JSI/ExpoModulesHostObject.mm +1 -1
  113. package/ios/JSI/JavaScriptValue.swift +28 -1
  114. package/ios/ModuleRegistry/EXModuleRegistry.h +0 -4
  115. package/ios/ModuleRegistry/EXModuleRegistry.m +0 -23
  116. package/ios/ModuleRegistryAdapter/EXModuleRegistryAdapter.m +0 -16
  117. package/ios/ModuleRegistryProvider/EXModuleRegistryProvider.m +0 -6
  118. package/ios/NativeModulesProxy/EXNativeModulesProxy.mm +1 -31
  119. package/ios/Swift/AppContext.swift +46 -6
  120. package/ios/Swift/Arguments/Convertible.swift +3 -3
  121. package/ios/Swift/Arguments/Convertibles.swift +5 -5
  122. package/ios/Swift/Classes/ClassComponent.swift +18 -12
  123. package/ios/Swift/Classes/ClassRegistry.swift +31 -0
  124. package/ios/Swift/Conversions.swift +19 -3
  125. package/ios/Swift/Convertibles/Convertibles+Color.swift +3 -3
  126. package/ios/Swift/Convertibles/Either.swift +6 -4
  127. package/ios/Swift/DynamicTypes/AnyDynamicType.swift +18 -2
  128. package/ios/Swift/DynamicTypes/DynamicArrayType.swift +3 -3
  129. package/ios/Swift/DynamicTypes/DynamicConvertibleType.swift +2 -2
  130. package/ios/Swift/DynamicTypes/DynamicEnumType.swift +1 -1
  131. package/ios/Swift/DynamicTypes/DynamicJavaScriptType.swift +27 -0
  132. package/ios/Swift/DynamicTypes/DynamicOptionalType.swift +9 -2
  133. package/ios/Swift/DynamicTypes/DynamicRawType.swift +1 -1
  134. package/ios/Swift/DynamicTypes/DynamicSharedObjectType.swift +16 -2
  135. package/ios/Swift/DynamicTypes/DynamicType.swift +6 -0
  136. package/ios/Swift/DynamicTypes/DynamicTypedArrayType.swift +15 -4
  137. package/ios/Swift/DynamicTypes/DynamicViewType.swift +68 -0
  138. package/ios/Swift/ExpoBridgeModule.swift +1 -1
  139. package/ios/Swift/Functions/AnyFunction.swift +5 -4
  140. package/ios/Swift/Functions/AsyncFunctionComponent.swift +22 -19
  141. package/ios/Swift/Functions/ConcurrentFunctionDefinition.swift +29 -13
  142. package/ios/Swift/Functions/SyncFunctionComponent.swift +26 -15
  143. package/ios/Swift/JavaScriptFunction.swift +68 -0
  144. package/ios/Swift/JavaScriptUtils.swift +57 -18
  145. package/ios/Swift/ModuleHolder.swift +22 -10
  146. package/ios/Swift/Modules/ModuleDefinition.swift +8 -2
  147. package/ios/Swift/Objects/JavaScriptObjectBuilder.swift +8 -8
  148. package/ios/Swift/Objects/ObjectDefinition.swift +17 -15
  149. package/ios/Swift/Objects/PropertyComponent.swift +23 -17
  150. package/ios/Swift/Records/AnyField.swift +1 -1
  151. package/ios/Swift/Records/Field.swift +2 -2
  152. package/ios/Swift/Records/Record.swift +5 -5
  153. package/ios/Swift/SharedObjects/SharedObjectRegistry.swift +4 -0
  154. package/ios/Swift/Views/AnyViewProp.swift +1 -1
  155. package/ios/Swift/Views/ComponentData.swift +37 -2
  156. package/ios/Swift/Views/ConcreteViewProp.swift +2 -2
  157. package/ios/Swift/Views/ViewDefinition.swift +39 -0
  158. package/ios/Swift/Views/ViewModuleWrapper.swift +0 -29
  159. package/ios/Tests/ClassComponentSpec.swift +39 -27
  160. package/ios/Tests/ConvertiblesSpec.swift +75 -49
  161. package/ios/Tests/DynamicTypeSpec.swift +29 -27
  162. package/ios/Tests/EitherSpec.swift +9 -7
  163. package/ios/Tests/ExpoModulesSpec.swift +13 -13
  164. package/ios/Tests/FunctionSpec.swift +38 -22
  165. package/ios/Tests/JavaScriptRuntimeSpec.swift +4 -0
  166. package/ios/Tests/PropertyComponentSpec.swift +33 -30
  167. package/ios/Tests/RecordSpec.swift +7 -5
  168. package/ios/Tests/SharedObjectRegistrySpec.swift +12 -12
  169. package/ios/Tests/TypedArraysSpec.swift +1 -1
  170. package/ios/Tests/ViewDefinitionSpec.swift +4 -2
  171. package/package.json +2 -2
  172. package/src/NativeViewManagerAdapter.native.tsx +33 -29
  173. package/src/requireNativeModule.ts +2 -2
  174. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinitionBuilder.kt +0 -132
  175. package/ios/EXViewManager.h +0 -21
  176. package/ios/EXViewManager.m +0 -128
  177. package/ios/ModuleRegistryAdapter/EXViewManagerAdapterClassesRegistry.h +0 -17
  178. package/ios/ModuleRegistryAdapter/EXViewManagerAdapterClassesRegistry.m +0 -67
  179. package/ios/ViewManagerAdapter/EXViewManagerAdapter.h +0 -17
  180. package/ios/ViewManagerAdapter/EXViewManagerAdapter.m +0 -45
@@ -2,16 +2,16 @@
2
2
 
3
3
  /**
4
4
  A protocol that allows custom classes or structs to be used as function arguments.
5
- It requires static `convert(from:)` function that knows how to convert incoming
5
+ It requires static `convert(from:appContext:)` function that knows how to convert incoming
6
6
  value of `Any` type to the type implemented by this protocol. It should throw an error
7
7
  when the value is not recognized, is invalid or doesn't meet type requirements.
8
8
  */
9
9
  public protocol Convertible: AnyArgument {
10
10
  /**
11
- Converts any value to the instance of its class (or struct).
11
+ Converts any value to the instance of its class (or struct) in the given app context.
12
12
  Throws an error when given value cannot be converted.
13
13
  */
14
- static func convert(from value: Any?) throws -> Self
14
+ static func convert(from value: Any?, appContext: AppContext) throws -> Self
15
15
  }
16
16
 
17
17
  @available(*, deprecated, renamed: "Convertible")
@@ -12,7 +12,7 @@ import CoreGraphics
12
12
  // MARK: - Foundation
13
13
 
14
14
  extension URL: Convertible {
15
- public static func convert(from value: Any?) throws -> Self {
15
+ public static func convert(from value: Any?, appContext: AppContext) throws -> Self {
16
16
  guard let value = value as? String else {
17
17
  throw Conversions.ConvertingException<URL>(value)
18
18
  }
@@ -47,7 +47,7 @@ internal class UrlContainsInvalidCharactersException: Exception {
47
47
  // MARK: - CoreGraphics
48
48
 
49
49
  extension CGPoint: Convertible {
50
- public static func convert(from value: Any?) throws -> CGPoint {
50
+ public static func convert(from value: Any?, appContext: AppContext) throws -> CGPoint {
51
51
  if let value = value as? [Double], value.count == 2 {
52
52
  return CGPoint(x: value[0], y: value[1])
53
53
  }
@@ -60,7 +60,7 @@ extension CGPoint: Convertible {
60
60
  }
61
61
 
62
62
  extension CGSize: Convertible {
63
- public static func convert(from value: Any?) throws -> CGSize {
63
+ public static func convert(from value: Any?, appContext: AppContext) throws -> CGSize {
64
64
  if let value = value as? [Double], value.count == 2 {
65
65
  return CGSize(width: value[0], height: value[1])
66
66
  }
@@ -73,7 +73,7 @@ extension CGSize: Convertible {
73
73
  }
74
74
 
75
75
  extension CGVector: Convertible {
76
- public static func convert(from value: Any?) throws -> CGVector {
76
+ public static func convert(from value: Any?, appContext: AppContext) throws -> CGVector {
77
77
  if let value = value as? [Double], value.count == 2 {
78
78
  return CGVector(dx: value[0], dy: value[1])
79
79
  }
@@ -86,7 +86,7 @@ extension CGVector: Convertible {
86
86
  }
87
87
 
88
88
  extension CGRect: Convertible {
89
- public static func convert(from value: Any?) throws -> CGRect {
89
+ public static func convert(from value: Any?, appContext: AppContext) throws -> CGRect {
90
90
  if let value = value as? [Double], value.count == 4 {
91
91
  return CGRect(x: value[0], y: value[1], width: value[2], height: value[3])
92
92
  }
@@ -37,35 +37,41 @@ public final class ClassComponent: ObjectDefinition {
37
37
 
38
38
  // MARK: - JavaScriptObjectBuilder
39
39
 
40
- public override func build(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject {
41
- let klass = runtime.createClass(name) { [weak self, weak runtime] this, arguments in
42
- guard let self = self, let runtime = runtime else {
40
+ public override func build(appContext: AppContext) throws -> JavaScriptObject {
41
+ let klass = try appContext.runtime.createClass(name) { [weak self, weak appContext] this, arguments in
42
+ guard let self = self, let appContext else {
43
43
  // TODO: Throw an exception? (@tsapeta)
44
44
  return
45
45
  }
46
- // The properties can't go into the prototype as they would be shared across all instances.
47
- // Instead, we decorate the instance object on initialization.
48
- self.decorateWithProperties(runtime: runtime, object: this)
49
46
 
50
47
  // Call the native constructor when defined.
51
- let result = try? self.constructor?.call(by: this, withArguments: arguments)
48
+ let result = try? self.constructor?.call(by: this, withArguments: arguments, appContext: appContext)
52
49
 
53
50
  // Register the shared object if returned by the constructor.
54
51
  if let result = result as? SharedObject {
55
52
  SharedObjectRegistry.add(native: result, javaScript: this)
56
53
  }
57
54
  }
58
- decorate(object: klass, inRuntime: runtime)
55
+
56
+ try decorate(object: klass, appContext: appContext)
57
+
58
+ // Register the JS class and its associated native type.
59
+ if let sharedObjectType = associatedType as? DynamicSharedObjectType {
60
+ appContext.classRegistry.register(nativeClassId: sharedObjectType.typeIdentifier, javaScriptClass: klass)
61
+ }
62
+
59
63
  return klass
60
64
  }
61
65
 
62
- public override func decorate(object: JavaScriptObject, inRuntime runtime: JavaScriptRuntime) {
66
+ public override func decorate(object: JavaScriptObject, appContext: AppContext) throws {
63
67
  // Here we actually don't decorate the input object (constructor) but its prototype.
64
68
  // Properties are intentionally skipped here — they have to decorate an instance instead of the prototype.
65
69
  let prototype = object.getProperty("prototype").getObject()
66
- decorateWithConstants(runtime: runtime, object: prototype)
67
- decorateWithFunctions(runtime: runtime, object: prototype)
68
- decorateWithClasses(runtime: runtime, object: prototype)
70
+
71
+ decorateWithConstants(object: prototype)
72
+ try decorateWithFunctions(object: prototype, appContext: appContext)
73
+ try decorateWithClasses(object: prototype, appContext: appContext)
74
+ try decorateWithProperties(object: prototype, appContext: appContext)
69
75
  }
70
76
  }
71
77
 
@@ -0,0 +1,31 @@
1
+ // Copyright 2023-present 650 Industries. All rights reserved.
2
+
3
+ internal final class ClassRegistry {
4
+ var nativeToJS = [ObjectIdentifier: JavaScriptWeakObject]()
5
+
6
+ // MARK: - Accessing
7
+
8
+ func getJavaScriptClass(nativeClassId: ObjectIdentifier) -> JavaScriptObject? {
9
+ return nativeToJS[nativeClassId]?.lock()
10
+ }
11
+
12
+ func getJavaScriptClass(nativeClass: SharedObject.Type) -> JavaScriptObject? {
13
+ let nativeClassId = ObjectIdentifier(nativeClass)
14
+ return getJavaScriptClass(nativeClassId: nativeClassId)
15
+ }
16
+
17
+ // MARK: - Registration
18
+
19
+ func register(nativeClassId: ObjectIdentifier, javaScriptClass: JavaScriptObject) {
20
+ nativeToJS[nativeClassId] = javaScriptClass.createWeak()
21
+ }
22
+
23
+ func register(nativeClass: SharedObject.Type, javaScriptClass: JavaScriptObject) {
24
+ let nativeClassId = ObjectIdentifier(nativeClass)
25
+ register(nativeClassId: nativeClassId, javaScriptClass: javaScriptClass)
26
+ }
27
+
28
+ internal func clear() {
29
+ nativeToJS.removeAll()
30
+ }
31
+ }
@@ -151,15 +151,31 @@ internal final class Conversions {
151
151
  /**
152
152
  Converts the function result to the type compatible with JavaScript.
153
153
  */
154
- static func convertFunctionResult<ValueType>(_ value: ValueType?, runtime: JavaScriptRuntime? = nil) -> Any {
154
+ static func convertFunctionResult<ValueType>(
155
+ _ value: ValueType?,
156
+ appContext: AppContext? = nil,
157
+ dynamicType: AnyDynamicType? = nil
158
+ ) -> Any {
155
159
  if let value = value as? Record {
156
160
  return value.toDictionary()
157
161
  }
158
162
  if let value = value as? [Record] {
159
163
  return value.map { $0.toDictionary() }
160
164
  }
161
- if let runtime = runtime, let value = value as? JavaScriptObjectBuilder {
162
- return value.build(inRuntime: runtime)
165
+ if let appContext {
166
+ if let value = value as? JavaScriptObjectBuilder {
167
+ return try? value.build(appContext: appContext)
168
+ }
169
+
170
+ // If the returned value is a native shared object, create its JS representation and add the pair to the registry of shared objects.
171
+ if let value = value as? SharedObject, let dynamicType = dynamicType as? DynamicSharedObjectType {
172
+ guard let object = try? appContext.newObject(nativeClassId: dynamicType.typeIdentifier) else {
173
+ log.warn("Unable to create a JS object for \(dynamicType.description)")
174
+ return Optional<Any>.none
175
+ }
176
+ SharedObjectRegistry.add(native: value, javaScript: object)
177
+ return object
178
+ }
163
179
  }
164
180
  return value as Any
165
181
  }
@@ -1,7 +1,7 @@
1
1
  // Copyright 2022-present 650 Industries. All rights reserved.
2
2
 
3
3
  extension UIColor: Convertible {
4
- public static func convert(from value: Any?) throws -> Self {
4
+ public static func convert(from value: Any?, appContext: AppContext) throws -> Self {
5
5
  // swiftlint:disable force_cast
6
6
  if let value = value as? String {
7
7
  if let namedColorComponents = namedColors[value] {
@@ -21,10 +21,10 @@ extension UIColor: Convertible {
21
21
  }
22
22
 
23
23
  extension CGColor: Convertible {
24
- public static func convert(from value: Any?) throws -> Self {
24
+ public static func convert(from value: Any?, appContext: AppContext) throws -> Self {
25
25
  // swiftlint:disable force_cast
26
26
  do {
27
- return try UIColor.convert(from: value).cgColor as! Self
27
+ return try UIColor.convert(from: value, appContext: appContext).cgColor as! Self
28
28
  } catch _ as Conversions.ConvertingException<UIColor> {
29
29
  // Rethrow `ConvertingError` with proper type
30
30
  throw Conversions.ConvertingException<CGColor>(value)
@@ -50,14 +50,16 @@ open class Either<FirstType, SecondType>: Convertible {
50
50
 
51
51
  // MARK: - Convertible
52
52
 
53
- public class func convert(from value: Any?) throws -> Self {
54
- for type in dynamicTypes() {
53
+ public class func convert(from value: Any?, appContext: AppContext) throws -> Self {
54
+ let dynamicTypes = dynamicTypes()
55
+
56
+ for type in dynamicTypes {
55
57
  // Initialize the "either" when the current type can cast given value.
56
- if let value = try? type.cast(value) {
58
+ if let value = try? type.cast(value, appContext: appContext) {
57
59
  return Self(value)
58
60
  }
59
61
  }
60
- throw NeitherTypeException(Self.dynamicTypes())
62
+ throw NeitherTypeException(dynamicTypes)
61
63
  }
62
64
  }
63
65
 
@@ -17,10 +17,26 @@ public protocol AnyDynamicType: CustomStringConvertible {
17
17
  func equals(_ type: AnyDynamicType) -> Bool
18
18
 
19
19
  /**
20
- Casts given any value to the wrapped type and returns as `Any`.
20
+ Preliminarily casts the given JavaScriptValue to a non-JS value that the other `cast` function can handle.
21
+ It **must** be run on the thread used by the JavaScript runtime.
22
+ */
23
+ func cast(jsValue: JavaScriptValue, appContext: AppContext) throws -> Any
24
+
25
+ /**
26
+ Casts the given value to the wrapped type and returns it as `Any`.
21
27
  NOTE: It may not be just simple type-casting (e.g. when the wrapped type conforms to `Convertible`).
22
28
  */
23
- func cast<ValueType>(_ value: ValueType) throws -> Any
29
+ func cast<ValueType>(_ value: ValueType, appContext: AppContext) throws -> Any
30
+ }
31
+
32
+ extension AnyDynamicType {
33
+ func cast(jsValue: JavaScriptValue, appContext: AppContext) throws -> Any {
34
+ return jsValue.getRaw()
35
+ }
36
+
37
+ func cast<ValueType>(_ value: ValueType, appContext: AppContext) throws -> Any {
38
+ return value
39
+ }
24
40
  }
25
41
 
26
42
  // MARK: - Operators
@@ -21,13 +21,13 @@ internal struct DynamicArrayType: AnyDynamicType {
21
21
  return false
22
22
  }
23
23
 
24
- func cast<ValueType>(_ value: ValueType) throws -> Any {
24
+ func cast<ValueType>(_ value: ValueType, appContext: AppContext) throws -> Any {
25
25
  if let value = value as? [Any] {
26
- return try value.map { try elementType.cast($0) }
26
+ return try value.map { try elementType.cast($0, appContext: appContext) }
27
27
  }
28
28
  // We should probably throw an error if we get here. On the other side, the array type
29
29
  // requirement can be more loosen so we can try to arrayize values that are not arrays.
30
- return [try elementType.cast(value)]
30
+ return [try elementType.cast(value, appContext: appContext)]
31
31
  }
32
32
 
33
33
  var description: String {
@@ -17,8 +17,8 @@ internal struct DynamicConvertibleType: AnyDynamicType {
17
17
  return false
18
18
  }
19
19
 
20
- func cast<ValueType>(_ value: ValueType) throws -> Any {
21
- return try innerType.convert(from: value)
20
+ func cast<ValueType>(_ value: ValueType, appContext: AppContext) throws -> Any {
21
+ return try innerType.convert(from: value, appContext: appContext)
22
22
  }
23
23
 
24
24
  var description: String {
@@ -17,7 +17,7 @@ internal struct DynamicEnumType: AnyDynamicType {
17
17
  return false
18
18
  }
19
19
 
20
- func cast<ValueType>(_ value: ValueType) throws -> Any {
20
+ func cast<ValueType>(_ value: ValueType, appContext: AppContext) throws -> Any {
21
21
  return try innerType.create(fromRawValue: value)
22
22
  }
23
23
 
@@ -0,0 +1,27 @@
1
+ // Copyright 2022-present 650 Industries. All rights reserved.
2
+
3
+ /**
4
+ A dynamic type representing various types of JavaScript values.
5
+ */
6
+ internal struct DynamicJavaScriptType: AnyDynamicType {
7
+ let innerType: AnyJavaScriptValue.Type
8
+
9
+ func wraps<InnerType>(_ type: InnerType.Type) -> Bool {
10
+ return innerType == InnerType.self
11
+ }
12
+
13
+ func equals(_ type: AnyDynamicType) -> Bool {
14
+ if let providedType = type as? Self {
15
+ return providedType.innerType == innerType
16
+ }
17
+ return false
18
+ }
19
+
20
+ func cast(jsValue: JavaScriptValue, appContext: AppContext) throws -> Any {
21
+ return try innerType.convert(from: jsValue, appContext: appContext)
22
+ }
23
+
24
+ var description: String {
25
+ return String(describing: innerType.self)
26
+ }
27
+ }
@@ -21,11 +21,18 @@ internal struct DynamicOptionalType: AnyDynamicType {
21
21
  return false
22
22
  }
23
23
 
24
- func cast<ValueType>(_ value: ValueType) throws -> Any {
24
+ func cast(jsValue: JavaScriptValue, appContext: AppContext) throws -> Any {
25
+ if jsValue.isUndefined() || jsValue.isNull() {
26
+ return Optional<Any>.none as Any
27
+ }
28
+ return try wrappedType.cast(jsValue: jsValue, appContext: appContext)
29
+ }
30
+
31
+ func cast<ValueType>(_ value: ValueType, appContext: AppContext) throws -> Any {
25
32
  if Optional.isNil(value) || value is NSNull {
26
33
  return Optional<Any>.none as Any
27
34
  }
28
- return try wrappedType.cast(value)
35
+ return try wrappedType.cast(value, appContext: appContext)
29
36
  }
30
37
 
31
38
  var description: String {
@@ -15,7 +15,7 @@ internal struct DynamicRawType<InnerType>: AnyDynamicType {
15
15
  return type is Self
16
16
  }
17
17
 
18
- func cast<ValueType>(_ value: ValueType) throws -> Any {
18
+ func cast<ValueType>(_ value: ValueType, appContext: AppContext) throws -> Any {
19
19
  if let value = value as? InnerType {
20
20
  return value
21
21
  }
@@ -6,6 +6,16 @@
6
6
  internal struct DynamicSharedObjectType: AnyDynamicType {
7
7
  let innerType: SharedObject.Type
8
8
 
9
+ /**
10
+ A unique identifier of the wrapped type.
11
+ */
12
+ let typeIdentifier: ObjectIdentifier
13
+
14
+ init<SharedObjectType: SharedObject>(innerType: SharedObjectType.Type) {
15
+ self.innerType = innerType
16
+ self.typeIdentifier = ObjectIdentifier(SharedObjectType.self)
17
+ }
18
+
9
19
  func wraps<InnerType>(_ type: InnerType.Type) -> Bool {
10
20
  return innerType == InnerType.self
11
21
  }
@@ -17,12 +27,16 @@ internal struct DynamicSharedObjectType: AnyDynamicType {
17
27
  return false
18
28
  }
19
29
 
20
- func cast<ValueType>(_ value: ValueType) throws -> Any {
30
+ func cast<ValueType>(_ value: ValueType, appContext: AppContext) throws -> Any {
21
31
  if let value = value as? SharedObject, type(of: value) == innerType {
22
32
  // Given value is a shared object already
23
33
  return value
24
34
  }
25
- if let jsObject = try (value as? JavaScriptValue)?.asObject(),
35
+ throw NativeSharedObjectNotFoundException()
36
+ }
37
+
38
+ func cast(jsValue: JavaScriptValue, appContext: AppContext) throws -> Any {
39
+ if let jsObject = try? jsValue.asObject(),
26
40
  let nativeSharedObject = SharedObjectRegistry.toNativeObject(jsObject) {
27
41
  return nativeSharedObject
28
42
  }
@@ -21,12 +21,18 @@ internal func DynamicType<T>(_ type: T.Type) -> AnyDynamicType {
21
21
  if let EnumType = T.self as? any Enumerable.Type {
22
22
  return DynamicEnumType(innerType: EnumType)
23
23
  }
24
+ if let ViewType = T.self as? UIView.Type {
25
+ return DynamicViewType(innerType: ViewType)
26
+ }
24
27
  if let SharedObjectType = T.self as? SharedObject.Type {
25
28
  return DynamicSharedObjectType(innerType: SharedObjectType)
26
29
  }
27
30
  if let TypedArrayType = T.self as? AnyTypedArray.Type {
28
31
  return DynamicTypedArrayType(innerType: TypedArrayType)
29
32
  }
33
+ if let JavaScriptValueType = T.self as? any AnyJavaScriptValue.Type {
34
+ return DynamicJavaScriptType(innerType: JavaScriptValueType)
35
+ }
30
36
  return DynamicRawType(innerType: T.self)
31
37
  }
32
38
 
@@ -14,13 +14,24 @@ internal struct DynamicTypedArrayType: AnyDynamicType {
14
14
  return false
15
15
  }
16
16
 
17
- func cast<ValueType>(_ value: ValueType) throws -> Any {
18
- // It must be a JavaScript typed array.
19
- guard let value = value as? JavaScriptValue, let jsTypedArray = value.getTypedArray() else {
17
+ /**
18
+ Converts JS typed array to its native representation.
19
+ */
20
+ func cast(jsValue: JavaScriptValue, appContext: AppContext) throws -> Any {
21
+ guard let jsTypedArray = jsValue.getTypedArray() else {
20
22
  throw NotTypedArrayException(innerType)
21
23
  }
22
- let typedArray = TypedArray.create(from: jsTypedArray)
24
+ return TypedArray.create(from: jsTypedArray)
25
+ }
23
26
 
27
+ /**
28
+ Converts the given native `TypedArray` to a concrete typed array class wrapped by the dynamic type.
29
+ Throws `ArrayTypeMismatchException` otherwise.
30
+ */
31
+ func cast<ValueType>(_ value: ValueType, appContext: AppContext) throws -> Any {
32
+ guard let typedArray = value as? TypedArray else {
33
+ throw NotTypedArrayException(innerType)
34
+ }
24
35
  // Concrete typed arrays must be the same as the inner type.
25
36
  guard innerType == TypedArray.self || type(of: typedArray) == innerType else {
26
37
  throw ArrayTypeMismatchException((received: type(of: typedArray), expected: innerType))
@@ -0,0 +1,68 @@
1
+ // Copyright 2023-present 650 Industries. All rights reserved.
2
+
3
+ internal struct DynamicViewType: AnyDynamicType {
4
+ let innerType: UIView.Type
5
+
6
+ func wraps<InnerType>(_ type: InnerType.Type) -> Bool {
7
+ return innerType == InnerType.self
8
+ }
9
+
10
+ func equals(_ type: AnyDynamicType) -> Bool {
11
+ if let viewType = type as? Self {
12
+ return viewType.innerType == innerType
13
+ }
14
+ return false
15
+ }
16
+
17
+ /**
18
+ Casts from the React component instance to the view tag (`Int`).
19
+ */
20
+ func cast(jsValue: JavaScriptValue, appContext: AppContext) throws -> Any {
21
+ guard let viewTag = findViewTag(jsValue) else {
22
+ throw InvalidViewTagException()
23
+ }
24
+ return viewTag
25
+ }
26
+
27
+ /**
28
+ Converts a value of type `Int` to a native view with that tag in the given app context.
29
+ */
30
+ func cast<ValueType>(_ value: ValueType, appContext: AppContext) throws -> Any {
31
+ guard let viewTag = value as? Int else {
32
+ throw InvalidViewTagException()
33
+ }
34
+ guard Thread.isMainThread else {
35
+ throw NonMainThreadException()
36
+ }
37
+ guard let view = appContext.findView(withTag: viewTag, ofType: innerType.self) else {
38
+ throw Exceptions.ViewNotFound((tag: viewTag, type: innerType.self))
39
+ }
40
+ return view
41
+ }
42
+
43
+ var description: String {
44
+ return "View<\(innerType)>"
45
+ }
46
+ }
47
+
48
+ private func findViewTag(_ value: JavaScriptValue) -> Int? {
49
+ if let viewTag = value as? Int {
50
+ return viewTag
51
+ }
52
+ if let value = value as? JavaScriptValue, value.isObject() {
53
+ return value.getObject().getProperty("nativeTag").getInt()
54
+ }
55
+ return nil
56
+ }
57
+
58
+ internal final class InvalidViewTagException: Exception {
59
+ override var reason: String {
60
+ "The view tag must be a number"
61
+ }
62
+ }
63
+
64
+ internal final class NonMainThreadException: Exception {
65
+ override var reason: String {
66
+ "All operations on the views must run from the main UI thread"
67
+ }
68
+ }
@@ -46,7 +46,7 @@ public final class ExpoBridgeModule: NSObject, RCTBridgeModule {
46
46
  guard let self = self, let bridge = self.appContext.reactBridge else {
47
47
  return
48
48
  }
49
- self.appContext.runtime = EXJavaScriptRuntimeManager.runtime(fromBridge: bridge)
49
+ self.appContext._runtime = EXJavaScriptRuntimeManager.runtime(fromBridge: bridge)
50
50
  }
51
51
 
52
52
  if bridge.responds(to: Selector(("runtime"))) {
@@ -41,9 +41,10 @@ internal protocol AnyFunction: AnyDefinition, JavaScriptObjectBuilder {
41
41
  - args: An array of arguments to pass to the function. They could be Swift primitives
42
42
  when invoked through the bridge and in unit tests or `JavaScriptValue`s
43
43
  when the function is called through the JSI.
44
+ - appContext: An app context where the function is being executed.
44
45
  - callback: A callback that receives a result of the function execution.
45
46
  */
46
- func call(by owner: AnyObject?, withArguments args: [Any], callback: @escaping (FunctionCallResult) -> ())
47
+ func call(by owner: AnyObject?, withArguments args: [Any], appContext: AppContext, callback: @escaping (FunctionCallResult) -> ())
47
48
  }
48
49
 
49
50
  extension AnyFunction {
@@ -65,12 +66,12 @@ extension AnyFunction {
65
66
  }
66
67
 
67
68
  /**
68
- Calls the function just like `call(by:withArguments:callback:)`, but without an owner
69
+ Calls the function just like `call(by:withArguments:appContext:callback:)`, but without an owner
69
70
  and with an empty callback. Might be useful when you only want to call the function,
70
71
  but don't care about the result.
71
72
  */
72
- func call(withArguments args: [Any]) {
73
- call(by: nil, withArguments: args, callback: { _ in })
73
+ func call(withArguments args: [Any], appContext: AppContext) {
74
+ call(by: nil, withArguments: args, appContext: appContext, callback: { _ in })
74
75
  }
75
76
  }
76
77
 
@@ -46,10 +46,8 @@ public final class AsyncFunctionComponent<Args, FirstArgType, ReturnType>: AnyAs
46
46
  ) {
47
47
  self.name = name
48
48
  self.takesPromise = dynamicArgumentTypes.last?.wraps(Promise.self) ?? false
49
+ self.dynamicArgumentTypes = dynamicArgumentTypes
49
50
  self.body = body
50
-
51
- // Drop the last argument type if it's the `Promise`.
52
- self.dynamicArgumentTypes = takesPromise ? dynamicArgumentTypes.dropLast(1) : dynamicArgumentTypes
53
51
  }
54
52
 
55
53
  // MARK: - AnyFunction
@@ -59,25 +57,30 @@ public final class AsyncFunctionComponent<Args, FirstArgType, ReturnType>: AnyAs
59
57
  let dynamicArgumentTypes: [AnyDynamicType]
60
58
 
61
59
  var argumentsCount: Int {
62
- return dynamicArgumentTypes.count - (takesOwner ? 1 : 0)
60
+ return dynamicArgumentTypes.count - (takesOwner ? 1 : 0) - (takesPromise ? 1 : 0)
63
61
  }
64
62
 
65
63
  var takesOwner: Bool = false
66
64
 
67
- func call(by owner: AnyObject?, withArguments args: [Any], callback: @escaping (FunctionCallResult) -> ()) {
65
+ func call(by owner: AnyObject?, withArguments args: [Any], appContext: AppContext, callback: @escaping (FunctionCallResult) -> ()) {
68
66
  let promise = Promise { value in
69
67
  callback(.success(Conversions.convertFunctionResult(value)))
70
68
  } rejecter: { exception in
71
69
  callback(.failure(exception))
72
70
  }
73
- var arguments: [Any] = []
71
+ var arguments: [Any] = concat(
72
+ arguments: args,
73
+ withOwner: owner,
74
+ withPromise: takesPromise ? promise : nil,
75
+ forFunction: self,
76
+ appContext: appContext
77
+ )
74
78
 
75
79
  do {
76
- arguments = concat(
77
- arguments: try cast(arguments: args, forFunction: self),
78
- withOwner: owner,
79
- forFunction: self
80
- )
80
+ try validateArgumentsNumber(function: self, received: args.count)
81
+
82
+ // All `JavaScriptValue` args must be preliminarly converted on the JS thread, so before we jump to the function's queue.
83
+ arguments = try cast(jsValues: arguments, forFunction: self, appContext: appContext)
81
84
  } catch let error as Exception {
82
85
  callback(.failure(error))
83
86
  return
@@ -86,18 +89,18 @@ public final class AsyncFunctionComponent<Args, FirstArgType, ReturnType>: AnyAs
86
89
  return
87
90
  }
88
91
 
89
- // Add promise to the array of arguments if necessary.
90
- if takesPromise {
91
- arguments.append(promise)
92
- }
93
-
94
92
  let queue = queue ?? defaultQueue
95
93
 
96
94
  queue.async { [body, name] in
97
95
  let returnedValue: ReturnType?
98
96
 
99
97
  do {
98
+ // Convert arguments to the types desired by the function.
99
+ arguments = try cast(arguments: arguments, forFunction: self, appContext: appContext)
100
+
101
+ // swiftlint:disable:next force_cast
100
102
  let argumentsTuple = try Conversions.toTuple(arguments) as! Args
103
+
101
104
  returnedValue = try body(argumentsTuple)
102
105
  } catch let error as Exception {
103
106
  promise.reject(FunctionCallException(name).causedBy(error))
@@ -114,13 +117,13 @@ public final class AsyncFunctionComponent<Args, FirstArgType, ReturnType>: AnyAs
114
117
 
115
118
  // MARK: - JavaScriptObjectBuilder
116
119
 
117
- func build(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject {
118
- return runtime.createAsyncFunction(name, argsCount: argumentsCount) { [weak self, name] this, args, resolve, reject in
120
+ func build(appContext: AppContext) throws -> JavaScriptObject {
121
+ return try appContext.runtime.createAsyncFunction(name, argsCount: argumentsCount) { [weak self, name] this, args, resolve, reject in
119
122
  guard let self = self else {
120
123
  let exception = NativeFunctionUnavailableException(name)
121
124
  return reject(exception.code, exception.description, nil)
122
125
  }
123
- self.call(by: this, withArguments: args) { result in
126
+ self.call(by: this, withArguments: args, appContext: appContext) { result in
124
127
  switch result {
125
128
  case .failure(let error):
126
129
  reject(error.code, error.description, nil)