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
@@ -0,0 +1,68 @@
1
+ // Copyright 2023-present 650 Industries. All rights reserved.
2
+
3
+ /**
4
+ Represents a JavaScript function that can be called by the native code and that must return the given generic `ReturnType`.
5
+ */
6
+ public final class JavaScriptFunction<ReturnType>: AnyArgument, AnyJavaScriptValue {
7
+ /**
8
+ Raw representation of the JavaScript function that doesn't impose any restrictions on the returned type.
9
+ */
10
+ private let rawFunction: RawJavaScriptFunction
11
+
12
+ /**
13
+ Weak reference to the app context that is necessary to convert some arguments associated with the context (e.g. shared objects).
14
+ */
15
+ private weak var appContext: AppContext?
16
+
17
+ init(rawFunction: RawJavaScriptFunction, appContext: AppContext) {
18
+ self.rawFunction = rawFunction
19
+ self.appContext = appContext
20
+ }
21
+
22
+ // MARK: - Calling
23
+
24
+ /**
25
+ Calls the function with the given `this` object and arguments.
26
+ */
27
+ public func call(_ arguments: Any..., usingThis this: JavaScriptObject? = nil) throws -> ReturnType {
28
+ return try call(withArguments: arguments, asConstructor: false, usingThis: this)
29
+ }
30
+
31
+ /**
32
+ Calls the function as a constructor with the given arguments. It's like calling a function with the `new` keyword.
33
+ */
34
+ public func callAsConstructor(_ arguments: Any...) throws -> ReturnType {
35
+ return try call(withArguments: arguments, asConstructor: true, usingThis: nil)
36
+ }
37
+
38
+ /**
39
+ Universal function that calls the function with given arguments, this object and whether to call it as a constructor.
40
+ */
41
+ private func call(withArguments arguments: [Any] = [], asConstructor: Bool = false, usingThis this: JavaScriptObject? = nil) throws -> ReturnType {
42
+ guard let appContext else {
43
+ throw AppContextLostException()
44
+ }
45
+ let value = rawFunction.call(withArguments: arguments, thisObject: this, asConstructor: false)
46
+ let dynamicType = ~ReturnType.self
47
+
48
+ guard let result = try dynamicType.cast(jsValue: value, appContext: appContext) as? ReturnType else {
49
+ throw UnexpectedReturnType(dynamicType.description)
50
+ }
51
+ return result
52
+ }
53
+
54
+ // MARK: - AnyJavaScriptValue
55
+
56
+ internal static func convert(from value: JavaScriptValue, appContext: AppContext) throws -> Self {
57
+ guard value.kind == .function else {
58
+ throw Conversions.ConvertingException<JavaScriptFunction<ReturnType>>(value)
59
+ }
60
+ return Self(rawFunction: value.getFunction(), appContext: appContext)
61
+ }
62
+ }
63
+
64
+ private final class UnexpectedReturnType: GenericException<String> {
65
+ override var reason: String {
66
+ return "The function returned a value that cannot be converted to \(param)"
67
+ }
68
+ }
@@ -10,12 +10,12 @@
10
10
  - Returns: A new value converted according to the dynamic type.
11
11
  - Throws: Rethrows various exceptions that could be thrown by the dynamic types.
12
12
  */
13
- internal func cast(_ value: Any, toType type: AnyDynamicType) throws -> Any {
13
+ internal func cast(_ value: Any, toType type: AnyDynamicType, appContext: AppContext) throws -> Any {
14
14
  // TODO: Accept JavaScriptValue and JavaScriptObject as argument types.
15
15
  if !(type is DynamicTypedArrayType), let value = value as? JavaScriptValue {
16
- return try type.cast(value.getRaw())
16
+ return try type.cast(value.getRaw(), appContext: appContext)
17
17
  }
18
- return try type.cast(value)
18
+ return try type.cast(value, appContext: appContext)
19
19
  }
20
20
 
21
21
  /**
@@ -23,46 +23,85 @@ internal func cast(_ value: Any, toType type: AnyDynamicType) throws -> Any {
23
23
  - Parameters:
24
24
  - arguments: An array of arguments to be cast.
25
25
  - function: A function for which to cast the arguments.
26
+ - appContext: A context of the app.
26
27
  - Returns: An array of arguments after casting. Its size is the same as the input arrays.
27
28
  - Throws: `InvalidArgsNumberException` when the number of arguments is not equal to the actual number
28
29
  of function's arguments (without an owner and promise). Rethrows exceptions thrown by `cast(_:toType:)`.
29
30
  */
30
- internal func cast(arguments: [Any], forFunction function: AnyFunction) throws -> [Any] {
31
- let requiredArgumentsCount = function.requiredArgumentsCount
32
- let argumentTypeOffset = function.takesOwner ? 1 : 0
33
-
34
- if arguments.count < requiredArgumentsCount || arguments.count > function.argumentsCount {
35
- throw InvalidArgsNumberException((
36
- received: arguments.count,
37
- expected: function.argumentsCount,
38
- required: requiredArgumentsCount
39
- ))
40
- }
31
+ internal func cast(arguments: [Any], forFunction function: AnyFunction, appContext: AppContext) throws -> [Any] {
41
32
  return try arguments.enumerated().map { index, argument in
42
- let argumentType = function.dynamicArgumentTypes[index + argumentTypeOffset]
33
+ let argumentType = function.dynamicArgumentTypes[index]
43
34
 
44
35
  do {
45
- return try cast(argument, toType: argumentType)
36
+ return try cast(argument, toType: argumentType, appContext: appContext)
46
37
  } catch {
47
38
  throw ArgumentCastException((index: index, type: argumentType)).causedBy(error)
48
39
  }
49
40
  }
50
41
  }
51
42
 
43
+ /**
44
+ Casts an array of JavaScript values to non-JavaScript types.
45
+ */
46
+ internal func cast(jsValues: [Any], forFunction function: AnyFunction, appContext: AppContext) throws -> [Any] {
47
+ // TODO: Replace `[Any]` with `[JavaScriptValue]` once we make sure only JS values are passed here
48
+ return try jsValues.enumerated().map { index, jsValue in
49
+ let type = function.dynamicArgumentTypes[index]
50
+
51
+ do {
52
+ // Temporarily some values might already be cast to primitive types, so make sure we cast only `JavaScriptValue` and leave the others as they are.
53
+ if let jsValue = jsValue as? JavaScriptValue {
54
+ return try type.cast(jsValue: jsValue, appContext: appContext)
55
+ } else {
56
+ return jsValue
57
+ }
58
+ } catch {
59
+ throw ArgumentCastException((index: index, type: type)).causedBy(error)
60
+ }
61
+ }
62
+ }
63
+
64
+ /**
65
+ Validates whether the number of received arguments is enough to call the given function.
66
+ Throws `InvalidArgsNumberException` otherwise.
67
+ */
68
+ internal func validateArgumentsNumber(function: AnyFunction, received: Int) throws {
69
+ let argumentsCount = function.argumentsCount
70
+ let requiredArgumentsCount = function.requiredArgumentsCount
71
+
72
+ if received < requiredArgumentsCount || received > argumentsCount {
73
+ throw InvalidArgsNumberException((
74
+ received: received,
75
+ expected: argumentsCount,
76
+ required: requiredArgumentsCount
77
+ ))
78
+ }
79
+ }
80
+
52
81
  /**
53
82
  Ensures the provided array of arguments matches the number of arguments expected by the function.
54
83
  - If the function takes the owner, it's added to the beginning.
55
84
  - If the array is still too small, missing arguments are very likely to be optional so it puts `nil` in their place.
56
85
  */
57
- internal func concat(arguments: [Any], withOwner owner: AnyObject?, forFunction function: AnyFunction) -> [Any] {
86
+ internal func concat(
87
+ arguments: [Any],
88
+ withOwner owner: AnyObject?,
89
+ withPromise promise: Promise?,
90
+ forFunction function: AnyFunction,
91
+ appContext: AppContext
92
+ ) -> [Any] {
58
93
  var result = arguments
59
94
 
60
- if function.takesOwner, let owner = try? function.dynamicArgumentTypes.first?.cast(owner) {
95
+ if function.takesOwner {
61
96
  result = [owner] + arguments
62
97
  }
63
98
  if arguments.count < function.argumentsCount {
64
99
  result += Array(repeating: Any?.none as Any, count: function.argumentsCount - arguments.count)
65
100
  }
101
+ // Add promise to the array of arguments if necessary.
102
+ if let promise {
103
+ result += [promise]
104
+ }
66
105
  return result
67
106
  }
68
107
 
@@ -62,24 +62,28 @@ public final class ModuleHolder {
62
62
  // MARK: Calling functions
63
63
 
64
64
  func call(function functionName: String, args: [Any], _ callback: @escaping (FunctionCallResult) -> () = { _ in }) {
65
+ guard let appContext else {
66
+ callback(.failure(Exceptions.AppContextLost()))
67
+ return
68
+ }
65
69
  guard let function = definition.functions[functionName] else {
66
70
  callback(.failure(FunctionNotFoundException((functionName: functionName, moduleName: self.name))))
67
71
  return
68
72
  }
69
- function.call(by: self, withArguments: args, callback: callback)
73
+ function.call(by: self, withArguments: args, appContext: appContext, callback: callback)
70
74
  }
71
75
 
72
76
  @discardableResult
73
77
  func callSync(function functionName: String, args: [Any]) -> Any? {
74
- guard let function = definition.functions[functionName] as? AnySyncFunctionComponent else {
78
+ guard let appContext, let function = definition.functions[functionName] as? AnySyncFunctionComponent else {
75
79
  return nil
76
80
  }
77
81
  do {
78
- let arguments = try cast(arguments: args, forFunction: function)
79
- let result = try function.call(by: self, withArguments: arguments)
82
+ let arguments = try cast(arguments: args, forFunction: function, appContext: appContext)
83
+ let result = try function.call(by: self, withArguments: arguments, appContext: appContext)
80
84
 
81
85
  if let result = result as? SharedObject {
82
- let jsObject = SharedObjectRegistry.ensureSharedJavaScriptObject(runtime: appContext!.runtime!, nativeObject: result)
86
+ let jsObject = SharedObjectRegistry.ensureSharedJavaScriptObject(runtime: try appContext.runtime, nativeObject: result)
83
87
  return jsObject
84
88
  }
85
89
  return result
@@ -98,11 +102,16 @@ public final class ModuleHolder {
98
102
  */
99
103
  private func createJavaScriptModuleObject() -> JavaScriptObject? {
100
104
  // It might be impossible to create any object at the moment (e.g. remote debugging, app context destroyed)
101
- guard let runtime = appContext?.runtime else {
105
+ guard let appContext else {
106
+ return nil
107
+ }
108
+ do {
109
+ log.info("Creating JS object for module '\(name)'")
110
+ return try definition.build(appContext: appContext)
111
+ } catch {
112
+ log.error("Building the module object failed: \(error)")
102
113
  return nil
103
114
  }
104
- log.info("Creating JS object for module '\(name)'")
105
- return definition.build(inRuntime: runtime)
106
115
  }
107
116
 
108
117
  // MARK: Listening to native events
@@ -131,10 +140,13 @@ public final class ModuleHolder {
131
140
  Modifies module's listeners count and calls `onStartObserving` or `onStopObserving` accordingly.
132
141
  */
133
142
  func modifyListenersCount(_ count: Int) {
143
+ guard let appContext else {
144
+ return
145
+ }
134
146
  if count > 0 && listenersCount == 0 {
135
- definition.functions["startObserving"]?.call(withArguments: [])
147
+ definition.functions["startObserving"]?.call(withArguments: [], appContext: appContext)
136
148
  } else if count < 0 && listenersCount + count <= 0 {
137
- definition.functions["stopObserving"]?.call(withArguments: [])
149
+ definition.functions["stopObserving"]?.call(withArguments: [], appContext: appContext)
138
150
  }
139
151
  listenersCount = max(0, listenersCount + count)
140
152
  }
@@ -65,8 +65,14 @@ public final class ModuleDefinition: ObjectDefinition {
65
65
  return self
66
66
  }
67
67
 
68
- public override func build(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject {
69
- let object = super.build(inRuntime: runtime)
68
+ public override func build(appContext: AppContext) throws -> JavaScriptObject {
69
+ let object = try super.build(appContext: appContext)
70
+
71
+ if let viewManager {
72
+ let reactComponentPrototype = try appContext.runtime.createObject()
73
+ try viewManager.decorateWithFunctions(object: reactComponentPrototype, appContext: appContext)
74
+ object.setProperty("ViewPrototype", value: reactComponentPrototype)
75
+ }
70
76
 
71
77
  // Give the module object a name. It's used for compatibility reasons, see `EventEmitter.ts`.
72
78
  object.defineProperty("__expo_module_name__", value: name, options: [])
@@ -7,7 +7,7 @@ internal protocol JavaScriptObjectDecorator {
7
7
  /**
8
8
  Decorates an existing `JavaScriptObject`.
9
9
  */
10
- func decorate(object: JavaScriptObject, inRuntime runtime: JavaScriptRuntime)
10
+ func decorate(object: JavaScriptObject, appContext: AppContext) throws
11
11
  }
12
12
 
13
13
  /**
@@ -15,23 +15,23 @@ internal protocol JavaScriptObjectDecorator {
15
15
  */
16
16
  internal protocol JavaScriptObjectBuilder: JavaScriptObjectDecorator {
17
17
  /**
18
- Creates a decorated `JavaScriptObject` in the given runtime.
18
+ Creates a decorated `JavaScriptObject` in the given app context.
19
19
  */
20
- func build(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject
20
+ func build(appContext: AppContext) throws -> JavaScriptObject
21
21
  }
22
22
 
23
23
  /**
24
24
  Provides the default behavior of `JavaScriptObjectBuilder`.
25
- The `build(inRuntime:)` creates a plain object and uses `decorate(object:)` for decoration.
25
+ The `build(appContext:)` creates a plain object and uses `decorate(object:appContext:)` for decoration.
26
26
  */
27
27
  extension JavaScriptObjectBuilder {
28
- func build(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject {
29
- let object = runtime.createObject()
30
- decorate(object: object, inRuntime: runtime)
28
+ func build(appContext: AppContext) throws -> JavaScriptObject {
29
+ let object = try appContext.runtime.createObject()
30
+ try decorate(object: object, appContext: appContext)
31
31
  return object
32
32
  }
33
33
 
34
- func decorate(object: JavaScriptObject, inRuntime runtime: JavaScriptRuntime) {
34
+ func decorate(object: JavaScriptObject, appContext: AppContext) throws {
35
35
  // no-op by default
36
36
  }
37
37
  }
@@ -61,43 +61,45 @@ public class ObjectDefinition: AnyDefinition, JavaScriptObjectBuilder {
61
61
 
62
62
  // MARK: - JavaScriptObjectBuilder
63
63
 
64
- public func build(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject {
65
- let object = runtime.createObject()
66
- decorate(object: object, inRuntime: runtime)
64
+ public func build(appContext: AppContext) throws -> JavaScriptObject {
65
+ let object = try appContext.runtime.createObject()
66
+ try decorate(object: object, appContext: appContext)
67
67
  return object
68
68
  }
69
69
 
70
- public func decorate(object: JavaScriptObject, inRuntime runtime: JavaScriptRuntime) {
71
- decorateWithConstants(runtime: runtime, object: object)
72
- decorateWithFunctions(runtime: runtime, object: object)
73
- decorateWithProperties(runtime: runtime, object: object)
74
- decorateWithClasses(runtime: runtime, object: object)
70
+ public func decorate(object: JavaScriptObject, appContext: AppContext) throws {
71
+ let runtime = try appContext.runtime
72
+
73
+ decorateWithConstants(object: object)
74
+ try decorateWithFunctions(object: object, appContext: appContext)
75
+ try decorateWithProperties(object: object, appContext: appContext)
76
+ try decorateWithClasses(object: object, appContext: appContext)
75
77
  }
76
78
 
77
79
  // MARK: - Internals
78
80
 
79
- internal func decorateWithConstants(runtime: JavaScriptRuntime, object: JavaScriptObject) {
81
+ internal func decorateWithConstants(object: JavaScriptObject) {
80
82
  for (key, value) in getConstants() {
81
83
  object.setProperty(key, value: value)
82
84
  }
83
85
  }
84
86
 
85
- internal func decorateWithFunctions(runtime: JavaScriptRuntime, object: JavaScriptObject) {
87
+ internal func decorateWithFunctions(object: JavaScriptObject, appContext: AppContext) throws {
86
88
  for fn in functions.values {
87
- object.setProperty(fn.name, value: fn.build(inRuntime: runtime))
89
+ object.setProperty(fn.name, value: try fn.build(appContext: appContext))
88
90
  }
89
91
  }
90
92
 
91
- internal func decorateWithProperties(runtime: JavaScriptRuntime, object: JavaScriptObject) {
93
+ internal func decorateWithProperties(object: JavaScriptObject, appContext: AppContext) throws {
92
94
  for property in properties.values {
93
- let descriptor = property.buildDescriptor(inRuntime: runtime)
95
+ let descriptor = try property.buildDescriptor(appContext: appContext)
94
96
  object.defineProperty(property.name, descriptor: descriptor)
95
97
  }
96
98
  }
97
99
 
98
- internal func decorateWithClasses(runtime: JavaScriptRuntime, object: JavaScriptObject) {
100
+ internal func decorateWithClasses(object: JavaScriptObject, appContext: AppContext) throws {
99
101
  for klass in classes.values {
100
- object.setProperty(klass.name, value: klass.build(inRuntime: runtime))
102
+ object.setProperty(klass.name, value: try klass.build(appContext: appContext))
101
103
  }
102
104
  }
103
105
  }
@@ -9,7 +9,7 @@ protocol AnyPropertyComponent {
9
9
  /**
10
10
  Creates the JavaScript object representing the property descriptor.
11
11
  */
12
- func buildDescriptor(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject
12
+ func buildDescriptor(appContext: AppContext) throws -> JavaScriptObject
13
13
  }
14
14
 
15
15
  public final class PropertyComponent<OwnerType>: AnyDefinition, AnyPropertyComponent {
@@ -119,60 +119,66 @@ public final class PropertyComponent<OwnerType>: AnyDefinition, AnyPropertyCompo
119
119
 
120
120
  // MARK: - Internals
121
121
 
122
- internal func getValue<ValueType>(owner: OwnerType? = nil) throws -> ValueType? {
122
+ internal func getValue<ValueType>(owner: OwnerType? = nil, appContext: AppContext) throws -> ValueType? {
123
123
  let owner = owner as? AnyObject
124
- let value = try getter?.call(by: owner, withArguments: [])
124
+ let value = try getter?.call(by: owner, withArguments: [], appContext: appContext)
125
125
  return value as? ValueType
126
126
  }
127
127
 
128
- internal func setValue(_ value: Any, owner: OwnerType? = nil) {
128
+ internal func setValue(_ value: Any, owner: OwnerType? = nil, appContext: AppContext) {
129
129
  let owner = owner as? AnyObject
130
- _ = try? setter?.call(by: owner, withArguments: [value])
130
+ _ = try? setter?.call(by: owner, withArguments: [value], appContext: appContext)
131
131
  }
132
132
 
133
133
  /**
134
134
  Creates the JavaScript function that will be used as a getter of the property.
135
135
  */
136
- internal func buildGetter(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject {
137
- return runtime.createSyncFunction(name, argsCount: 0) { [weak self, name] this, args in
138
- guard let self = self else {
136
+ internal func buildGetter(appContext: AppContext) throws -> JavaScriptObject {
137
+ return try appContext.runtime.createSyncFunction(name, argsCount: 0) { [weak appContext, weak self, name] this, args in
138
+ guard let appContext else {
139
+ throw Exceptions.AppContextLost()
140
+ }
141
+ guard let self else {
139
142
  throw NativePropertyUnavailableException(name)
140
143
  }
141
144
  guard let getter = self.getter else {
142
145
  return
143
146
  }
144
- return try getter.call(by: this, withArguments: args)
147
+ return try getter.call(by: this, withArguments: args, appContext: appContext)
145
148
  }
146
149
  }
147
150
 
148
151
  /**
149
152
  Creates the JavaScript function that will be used as a setter of the property.
150
153
  */
151
- internal func buildSetter(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject {
152
- return runtime.createSyncFunction(name, argsCount: 1) { [weak self, name] this, args in
153
- guard let self = self else {
154
+ internal func buildSetter(appContext: AppContext) throws -> JavaScriptObject {
155
+ return try appContext.runtime.createSyncFunction(name, argsCount: 1) { [weak appContext, weak self, name] this, args in
156
+ guard let appContext else {
157
+ throw Exceptions.AppContextLost()
158
+ }
159
+ guard let self else {
154
160
  throw NativePropertyUnavailableException(name)
155
161
  }
156
162
  guard let setter = self.setter else {
157
163
  return
158
164
  }
159
- return try setter.call(by: this, withArguments: args)
165
+ return try setter.call(by: this, withArguments: args, appContext: appContext)
160
166
  }
161
167
  }
162
168
 
163
169
  /**
164
170
  Creates the JavaScript object representing the property descriptor.
165
171
  */
166
- internal func buildDescriptor(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject {
167
- let descriptor = runtime.createObject()
172
+ internal func buildDescriptor(appContext: AppContext) throws -> JavaScriptObject {
173
+ let descriptor = try appContext.runtime.createObject()
168
174
 
169
175
  descriptor.setProperty("enumerable", value: true)
170
176
 
171
177
  if getter != nil {
172
- descriptor.setProperty("get", value: buildGetter(inRuntime: runtime))
178
+ descriptor.setProperty("get", value: try buildGetter(appContext: appContext))
173
179
  }
174
180
  if setter != nil {
175
- descriptor.setProperty("set", value: buildSetter(inRuntime: runtime))
181
+ descriptor.setProperty("set", value: try buildSetter(appContext: appContext))
176
182
  }
177
183
  return descriptor
178
184
  }
@@ -19,5 +19,5 @@ internal protocol AnyFieldInternal: AnyField {
19
19
  */
20
20
  var isRequired: Bool { get }
21
21
 
22
- func set(_ newValue: Any?) throws
22
+ func set(_ newValue: Any?, appContext: AppContext) throws
23
23
  }
@@ -74,12 +74,12 @@ public final class Field<Type>: AnyFieldInternal {
74
74
  /**
75
75
  Sets the wrapped value with a value of `Any` type.
76
76
  */
77
- internal func set(_ newValue: Any?) throws {
77
+ internal func set(_ newValue: Any?, appContext: AppContext) throws {
78
78
  if newValue == nil && (!isOptional || isRequired) {
79
79
  throw FieldRequiredException(key!)
80
80
  }
81
81
  do {
82
- if let value = try fieldType.cast(newValue) as? Type {
82
+ if let value = try fieldType.cast(newValue, appContext: appContext) as? Type {
83
83
  wrappedValue = value
84
84
  }
85
85
  } catch {
@@ -15,7 +15,7 @@ public protocol Record: Convertible {
15
15
  /**
16
16
  Initializes a record from given dictionary. Only members wrapped by `@Field` will be set in the object.
17
17
  */
18
- init(from: Dict) throws
18
+ init(from: Dict, appContext: AppContext) throws
19
19
 
20
20
  /**
21
21
  Converts the record back to the dictionary. Only members wrapped by `@Field` will be set in the dictionary.
@@ -27,14 +27,14 @@ public protocol Record: Convertible {
27
27
  Provides the default implementation of `Record` protocol.
28
28
  */
29
29
  public extension Record {
30
- static func convert(from value: Any?) throws -> Self {
30
+ static func convert(from value: Any?, appContext: AppContext) throws -> Self {
31
31
  if let value = value as? Dict {
32
- return try Self(from: value)
32
+ return try Self(from: value, appContext: appContext)
33
33
  }
34
34
  throw Conversions.ConvertingException<Self>(value)
35
35
  }
36
36
 
37
- init(from dict: Dict) throws {
37
+ init(from dict: Dict, appContext: AppContext) throws {
38
38
  self.init()
39
39
 
40
40
  let dictKeys = dict.keys
@@ -45,7 +45,7 @@ public extension Record {
45
45
  return
46
46
  }
47
47
  if dictKeys.contains(key) || field.isRequired {
48
- try field.set(dict[key])
48
+ try field.set(dict[key], appContext: appContext)
49
49
  }
50
50
  }
51
51
  }
@@ -126,4 +126,8 @@ public final class SharedObjectRegistry {
126
126
  }
127
127
  return createSharedJavaScriptObject(runtime: runtime, nativeObject: nativeObject)
128
128
  }
129
+
130
+ internal static func clear() {
131
+ pairs.removeAll()
132
+ }
129
133
  }
@@ -12,5 +12,5 @@ public protocol AnyViewProp: ViewManagerDefinitionComponent {
12
12
  /**
13
13
  Function that sets the underlying prop value for given view.
14
14
  */
15
- func set(value: Any, onView: UIView) throws
15
+ func set(value: Any, onView: UIView, appContext: AppContext) throws
16
16
  }
@@ -43,6 +43,37 @@ public final class ComponentData: RCTComponentData {
43
43
  return super.createPropBlock(propName, isShadowView: isShadowView)
44
44
  }
45
45
 
46
+ public override func setProps(_ props: [String: Any], forView view: RCTComponent) {
47
+ guard let view = view as? UIView else {
48
+ log.warn("Given view is not an UIView")
49
+ return
50
+ }
51
+ guard let viewManager = moduleHolder?.viewManager else {
52
+ log.warn("View manager '\(self.name)' not found")
53
+ return
54
+ }
55
+ guard let appContext = moduleHolder?.appContext else {
56
+ log.warn("App context has been lost")
57
+ return
58
+ }
59
+ let propsDict = viewManager.propsDict()
60
+ var remainingProps = props
61
+
62
+ for (key, prop) in propsDict {
63
+ let newValue = props[key] as Any
64
+
65
+ // TODO: @tsapeta: Figure out better way to rethrow errors from here.
66
+ try? prop.set(value: Conversions.fromNSObject(newValue), onView: view, appContext: appContext)
67
+
68
+ remainingProps.removeValue(forKey: key)
69
+ }
70
+
71
+ // Let the base class `RCTComponentData` handle all remaining props.
72
+ super.setProps(remainingProps, forView: view)
73
+
74
+ viewManager.callLifecycleMethods(withType: .didUpdateProps, forView: view)
75
+ }
76
+
46
77
  /**
47
78
  The base `RCTComponentData` class does some Objective-C dynamic calls in this function, but we don't
48
79
  need to do these slow operations since the Sweet API gives us necessary details without reflections.
@@ -52,8 +83,12 @@ public final class ComponentData: RCTComponentData {
52
83
  var directEvents: [String] = []
53
84
  let superClass: AnyClass? = managerClass.superclass()
54
85
 
55
- if let eventNames = moduleHolder?.viewManager?.eventNames {
56
- for eventName in eventNames {
86
+ if let viewManager = moduleHolder?.viewManager {
87
+ for prop in viewManager.props {
88
+ // `id` allows every type to be passed in
89
+ propTypes[prop.name] = "id"
90
+ }
91
+ for eventName in viewManager.eventNames {
57
92
  directEvents.append(RCTNormalizeInputEventName(eventName))
58
93
  propTypes[eventName] = "BOOL"
59
94
  }
@@ -32,13 +32,13 @@ public final class ConcreteViewProp<ViewType: UIView, PropType: AnyArgument>: An
32
32
  /**
33
33
  Function that sets the underlying prop value for given view.
34
34
  */
35
- public func set(value: Any, onView view: UIView) throws {
35
+ public func set(value: Any, onView view: UIView, appContext: AppContext) throws {
36
36
  // Method's signature must be type-erased to conform to `AnyViewProp` protocol.
37
37
  // Given view must be castable to the generic `ViewType` type.
38
38
  guard let view = view as? ViewType else {
39
39
  throw IncompatibleViewException((propName: name, viewType: ViewType.self))
40
40
  }
41
- guard let value = try propType.cast(value) as? PropType else {
41
+ guard let value = try propType.cast(value, appContext: appContext) as? PropType else {
42
42
  throw Conversions.CastingException<PropType>(value)
43
43
  }
44
44
  setter(view, value)