expo-modules-core 1.2.7 → 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 +28 -5
  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 +8 -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 +30 -23
  42. package/android/src/main/java/expo/modules/kotlin/ModuleRegistry.kt +0 -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 -3
  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 +6 -7
  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
@@ -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)
@@ -28,15 +28,22 @@ public final class ConcurrentFunctionDefinition<Args, FirstArgType, ReturnType>:
28
28
 
29
29
  var takesOwner: Bool = false
30
30
 
31
- func call(by owner: AnyObject?, withArguments args: [Any], callback: @escaping (FunctionCallResult) -> Void) {
32
- let arguments: [Any]
31
+ func call(by owner: AnyObject?, withArguments args: [Any], appContext: AppContext, callback: @escaping (FunctionCallResult) -> Void) {
32
+ var arguments: [Any]
33
33
 
34
34
  do {
35
+ try validateArgumentsNumber(function: self, received: args.count)
36
+
35
37
  arguments = concat(
36
- arguments: try cast(arguments: args, forFunction: self),
38
+ arguments: args,
37
39
  withOwner: owner,
38
- forFunction: self
40
+ withPromise: nil,
41
+ forFunction: self,
42
+ appContext: appContext
39
43
  )
44
+
45
+ // All `JavaScriptValue` args must be preliminarly converted on the JS thread, before we jump to the function's queue.
46
+ arguments = try cast(jsValues: args, forFunction: self, appContext: appContext)
40
47
  } catch let error as Exception {
41
48
  callback(.failure(error))
42
49
  return
@@ -50,9 +57,12 @@ public final class ConcurrentFunctionDefinition<Args, FirstArgType, ReturnType>:
50
57
  let result: Result<Any, Exception>
51
58
 
52
59
  do {
60
+ // Convert arguments to the types desired by the function.
61
+ let finalArguments = try cast(arguments: arguments, forFunction: self, appContext: appContext)
62
+
53
63
  // TODO: Right now we force cast the tuple in all types of functions, but we should throw another exception here.
54
64
  // swiftlint:disable force_cast
55
- let argumentsTuple = try Conversions.toTuple(arguments) as! Args
65
+ let argumentsTuple = try Conversions.toTuple(finalArguments) as! Args
56
66
  let returnValue = try await body(argumentsTuple)
57
67
 
58
68
  result = .success(returnValue)
@@ -68,18 +78,24 @@ public final class ConcurrentFunctionDefinition<Args, FirstArgType, ReturnType>:
68
78
 
69
79
  // MARK: - JavaScriptObjectBuilder
70
80
 
71
- func build(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject {
72
- return runtime.createAsyncFunction(name, argsCount: argumentsCount) { [weak self, name] this, args, resolve, reject in
73
- guard let self = self else {
81
+ func build(appContext: AppContext) throws -> JavaScriptObject {
82
+ return try appContext.runtime.createAsyncFunction(name, argsCount: argumentsCount) {
83
+ [weak appContext, weak self, name] this, args, resolve, reject in
84
+
85
+ guard let appContext else {
86
+ let exception = Exceptions.AppContextLost()
87
+ return reject(exception.code, exception.description, nil)
88
+ }
89
+ guard let self else {
74
90
  let exception = NativeFunctionUnavailableException(name)
75
91
  return reject(exception.code, exception.description, nil)
76
92
  }
77
- self.call(by: this, withArguments: args) { result in
93
+ self.call(by: this, withArguments: args, appContext: appContext) { result in
78
94
  switch result {
79
- case .failure(let error):
80
- reject(error.code, error.description, nil)
81
- case .success(let value):
82
- resolve(value)
95
+ case .failure(let error):
96
+ reject(error.code, error.description, nil)
97
+ case .success(let value):
98
+ resolve(value)
83
99
  }
84
100
  }
85
101
  }
@@ -10,9 +10,10 @@ internal protocol AnySyncFunctionComponent: AnyFunction {
10
10
  - owner: An object that calls this function. If the `takesOwner` property is true
11
11
  and type of the first argument matches the owner type, it's being passed as the argument.
12
12
  - args: An array of arguments to pass to the function. The arguments must be of the same type as in the underlying closure.
13
+ - appContext: An app context where the function is executed.
13
14
  - Returns: A value returned by the called function when succeeded or an error when it failed.
14
15
  */
15
- func call(by owner: AnyObject?, withArguments args: [Any]) throws -> Any
16
+ func call(by owner: AnyObject?, withArguments args: [Any], appContext: AppContext) throws -> Any
16
17
  }
17
18
 
18
19
  /**
@@ -49,9 +50,9 @@ public final class SyncFunctionComponent<Args, FirstArgType, ReturnType>: AnySyn
49
50
 
50
51
  var takesOwner: Bool = false
51
52
 
52
- func call(by owner: AnyObject?, withArguments args: [Any], callback: @escaping (FunctionCallResult) -> ()) {
53
+ func call(by owner: AnyObject?, withArguments args: [Any], appContext: AppContext, callback: @escaping (FunctionCallResult) -> ()) {
53
54
  do {
54
- let result = try call(by: owner, withArguments: args)
55
+ let result = try call(by: owner, withArguments: args, appContext: appContext)
55
56
  callback(.success(Conversions.convertFunctionResult(result)))
56
57
  } catch let error as Exception {
57
58
  callback(.failure(error))
@@ -62,16 +63,26 @@ public final class SyncFunctionComponent<Args, FirstArgType, ReturnType>: AnySyn
62
63
 
63
64
  // MARK: - AnySyncFunctionComponent
64
65
 
65
- func call(by owner: AnyObject?, withArguments args: [Any]) throws -> Any {
66
+ func call(by owner: AnyObject?, withArguments args: [Any], appContext: AppContext) throws -> Any {
66
67
  do {
67
- let arguments = concat(
68
- arguments: try cast(arguments: args, forFunction: self),
68
+ try validateArgumentsNumber(function: self, received: args.count)
69
+
70
+ var arguments = concat(
71
+ arguments: args,
69
72
  withOwner: owner,
70
- forFunction: self
73
+ withPromise: nil,
74
+ forFunction: self,
75
+ appContext: appContext
71
76
  )
77
+
78
+ // Convert JS values to non-JS native types.
79
+ arguments = try cast(jsValues: arguments, forFunction: self, appContext: appContext)
80
+
81
+ // Convert arguments to the types desired by the function.
82
+ arguments = try cast(arguments: arguments, forFunction: self, appContext: appContext)
83
+
72
84
  let argumentsTuple = try Conversions.toTuple(arguments) as! Args
73
- let result = try body(argumentsTuple)
74
- return Conversions.convertFunctionResult(result)
85
+ return try body(argumentsTuple)
75
86
  } catch let error as Exception {
76
87
  throw FunctionCallException(name).causedBy(error)
77
88
  } catch {
@@ -81,17 +92,17 @@ public final class SyncFunctionComponent<Args, FirstArgType, ReturnType>: AnySyn
81
92
 
82
93
  // MARK: - JavaScriptObjectBuilder
83
94
 
84
- func build(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject {
95
+ func build(appContext: AppContext) throws -> JavaScriptObject {
85
96
  // We intentionally capture a strong reference to `self`, otherwise the "detached" objects would
86
97
  // immediately lose the reference to the definition and thus the underlying native function.
87
98
  // It may potentially cause memory leaks, but at the time of writing this comment,
88
99
  // the native definition instance deallocates correctly when the JS VM triggers the garbage collector.
89
- return runtime.createSyncFunction(name, argsCount: argumentsCount) { [weak runtime, self] this, args in
90
- guard let runtime = runtime else {
91
- throw Exceptions.RuntimeLost()
100
+ return try appContext.runtime.createSyncFunction(name, argsCount: argumentsCount) { [weak appContext, self] this, args in
101
+ guard let appContext else {
102
+ throw Exceptions.AppContextLost()
92
103
  }
93
- let result = try self.call(by: this, withArguments: args)
94
- return Conversions.convertFunctionResult(result, runtime: runtime)
104
+ let result = try self.call(by: this, withArguments: args, appContext: appContext)
105
+ return Conversions.convertFunctionResult(result, appContext: appContext, dynamicType: ~ReturnType.self)
95
106
  }
96
107
  }
97
108
  }