expo-modules-core 0.7.0 → 0.9.1

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 (129) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +1 -1
  3. package/android/ExpoModulesCorePlugin.gradle +15 -0
  4. package/android/build.gradle +46 -32
  5. package/android/src/main/java/expo/modules/adapters/react/NativeModulesProxy.java +5 -5
  6. package/android/src/main/java/expo/modules/adapters/react/services/UIManagerModuleWrapper.java +13 -0
  7. package/android/src/main/java/expo/modules/core/ViewManager.java +9 -0
  8. package/android/src/main/java/expo/modules/core/interfaces/JavaScriptContextProvider.java +4 -0
  9. package/android/src/main/java/expo/modules/core/interfaces/ReactActivityHandler.java +37 -1
  10. package/android/src/main/java/expo/modules/core/interfaces/ReactNativeHostHandler.java +30 -0
  11. package/android/src/main/java/expo/modules/core/interfaces/services/UIManager.java +2 -0
  12. package/android/src/main/java/expo/modules/kotlin/AppContext.kt +13 -5
  13. package/android/src/main/java/expo/modules/kotlin/KPromiseWrapper.kt +2 -13
  14. package/android/src/main/java/expo/modules/kotlin/KotlinInteropModuleRegistry.kt +11 -5
  15. package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +5 -1
  16. package/android/src/main/java/expo/modules/kotlin/ModuleRegistry.kt +17 -0
  17. package/android/src/main/java/expo/modules/kotlin/callbacks/ViewCallback.kt +14 -3
  18. package/android/src/main/java/expo/modules/kotlin/events/EventEmitter.kt +13 -0
  19. package/android/src/main/java/expo/modules/kotlin/events/KModuleEventEmitterWrapper.kt +102 -0
  20. package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +25 -1
  21. package/android/src/main/java/expo/modules/kotlin/{methods/AnyMethod.kt → functions/AnyFunction.kt} +6 -5
  22. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunction.kt +15 -0
  23. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionBuilder.kt +170 -0
  24. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionWithPromise.kt +15 -0
  25. package/android/src/main/java/expo/modules/kotlin/functions/AsyncSuspendFunction.kt +36 -0
  26. package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +14 -0
  27. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +374 -41
  28. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionData.kt +2 -2
  29. package/android/src/main/java/expo/modules/kotlin/records/FieldValidator.kt +139 -0
  30. package/android/src/main/java/expo/modules/kotlin/records/Record.kt +0 -39
  31. package/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt +59 -10
  32. package/android/src/main/java/expo/modules/kotlin/records/Required.kt +5 -0
  33. package/android/src/main/java/expo/modules/kotlin/records/ValidationBinder.kt +110 -0
  34. package/android/src/main/java/expo/modules/kotlin/records/Validators.kt +61 -0
  35. package/android/src/main/java/expo/modules/kotlin/types/JSTypeConverter.kt +35 -0
  36. package/android/src/main/java/expo/modules/kotlin/types/JSTypeConverterHelper.kt +148 -0
  37. package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +9 -1
  38. package/android/src/main/java/expo/modules/kotlin/views/GroupViewManagerWrapper.kt +49 -0
  39. package/android/src/main/java/expo/modules/kotlin/views/ViewGroupDefinition.kt +18 -0
  40. package/android/src/main/java/expo/modules/kotlin/views/ViewGroupDefinitionBuilder.kt +114 -0
  41. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinition.kt +4 -1
  42. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinitionBuilder.kt +56 -5
  43. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerWrapperDelegate.kt +3 -0
  44. package/build/NativeModulesProxy.native.d.ts +0 -4
  45. package/build/NativeModulesProxy.native.d.ts.map +1 -1
  46. package/build/NativeModulesProxy.native.js +1 -14
  47. package/build/NativeModulesProxy.native.js.map +1 -1
  48. package/build/NativeModulesProxy.types.d.ts +0 -3
  49. package/build/NativeModulesProxy.types.d.ts.map +1 -1
  50. package/build/NativeModulesProxy.types.js.map +1 -1
  51. package/build/NativeViewManagerAdapter.native.d.ts.map +1 -1
  52. package/build/NativeViewManagerAdapter.native.js +9 -33
  53. package/build/NativeViewManagerAdapter.native.js.map +1 -1
  54. package/build/sweet/NativeErrorManager.js +1 -1
  55. package/build/sweet/NativeErrorManager.js.map +1 -1
  56. package/ios/AppDelegates/EXAppDelegatesLoader.m +4 -8
  57. package/ios/AppDelegates/ExpoAppDelegate.swift +4 -10
  58. package/ios/EXAppDefines.h +1 -0
  59. package/ios/EXAppDefines.m +6 -0
  60. package/ios/EXUtilities.h +2 -0
  61. package/ios/EXUtilities.m +12 -0
  62. package/ios/ExpoModulesCore.h +4 -0
  63. package/ios/ExpoModulesCore.podspec +4 -2
  64. package/ios/Interfaces/FileSystem/EXFileSystemInterface.h +1 -1
  65. package/ios/Interfaces/TaskManager/EXTaskServiceInterface.h +1 -0
  66. package/ios/JSI/{JSIConversions.h → EXJSIConversions.h} +4 -1
  67. package/ios/JSI/{JSIConversions.mm → EXJSIConversions.mm} +16 -5
  68. package/ios/JSI/{JSIInstaller.h → EXJSIInstaller.h} +3 -3
  69. package/ios/JSI/EXJSIInstaller.mm +17 -0
  70. package/ios/JSI/{ExpoModulesProxySpec.h → EXJSIUtils.h} +0 -9
  71. package/ios/JSI/{ExpoModulesProxySpec.mm → EXJSIUtils.mm} +4 -48
  72. package/ios/JSI/EXJavaScriptObject.h +97 -0
  73. package/ios/JSI/EXJavaScriptObject.mm +121 -0
  74. package/ios/JSI/{JavaScriptRuntime.h → EXJavaScriptRuntime.h} +27 -8
  75. package/ios/JSI/EXJavaScriptRuntime.mm +153 -0
  76. package/ios/JSI/EXJavaScriptValue.h +57 -0
  77. package/ios/JSI/EXJavaScriptValue.mm +166 -0
  78. package/ios/JSI/ExpoModulesHostObject.mm +2 -1
  79. package/ios/JSI/JavaScriptRuntime.swift +32 -0
  80. package/ios/JSI/JavaScriptValue.swift +94 -0
  81. package/ios/ModuleRegistryAdapter/EXModuleRegistryAdapter.m +1 -11
  82. package/ios/NativeModulesProxy/EXNativeModulesProxy.h +2 -2
  83. package/ios/NativeModulesProxy/EXNativeModulesProxy.mm +24 -22
  84. package/ios/ReactDelegates/EXReactCompatibleHelpers.h +18 -0
  85. package/ios/ReactDelegates/EXReactCompatibleHelpers.m +19 -0
  86. package/ios/ReactDelegates/ExpoReactDelegate.swift +2 -2
  87. package/ios/ReactDelegates/ExpoReactDelegateHandler.swift +1 -1
  88. package/ios/Swift/AppContext.swift +21 -2
  89. package/ios/Swift/Arguments/AnyArgumentType.swift +1 -1
  90. package/ios/Swift/Functions/AnyFunction.swift +5 -0
  91. package/ios/Swift/Functions/AsyncFunctionComponent.swift +182 -0
  92. package/ios/Swift/Functions/ConcreteFunction.swift +40 -68
  93. package/ios/Swift/Functions/SyncFunctionComponent.swift +181 -0
  94. package/ios/Swift/JavaScriptUtils.swift +56 -0
  95. package/ios/Swift/ModuleHolder.swift +17 -5
  96. package/ios/Swift/Modules/ModuleDefinitionComponents.swift +66 -44
  97. package/ios/Swift/ModulesProvider.swift +3 -10
  98. package/ios/Swift/Objects/ObjectDefinitionComponents.swift +49 -0
  99. package/ios/Swift/SwiftInteropBridge.swift +14 -5
  100. package/ios/Swift/Views/ComponentData.swift +2 -1
  101. package/ios/Swift/Views/ExpoView.swift +8 -0
  102. package/ios/Swift/Views/ViewManagerDefinitionComponents.swift +23 -0
  103. package/ios/Swift.h +5 -0
  104. package/ios/Tests/ArgumentTypeSpec.swift +2 -3
  105. package/ios/Tests/ConstantsSpec.swift +6 -7
  106. package/ios/Tests/ConvertiblesSpec.swift +2 -3
  107. package/ios/Tests/ExceptionsSpec.swift +2 -3
  108. package/ios/Tests/ExpoModulesSpec.swift +75 -0
  109. package/ios/Tests/FunctionSpec.swift +13 -15
  110. package/ios/Tests/FunctionWithConvertiblesSpec.swift +4 -5
  111. package/ios/Tests/JavaScriptObjectSpec.swift +97 -0
  112. package/ios/Tests/JavaScriptRuntimeSpec.swift +94 -0
  113. package/ios/Tests/ModuleEventListenersSpec.swift +15 -16
  114. package/ios/Tests/ModuleRegistrySpec.swift +2 -3
  115. package/ios/Tests/RecordSpec.swift +2 -3
  116. package/package.json +2 -2
  117. package/src/NativeModulesProxy.native.ts +2 -22
  118. package/src/NativeModulesProxy.types.ts +0 -8
  119. package/src/NativeViewManagerAdapter.native.tsx +12 -28
  120. package/src/sweet/NativeErrorManager.ts +1 -1
  121. package/android/src/main/java/expo/modules/kotlin/events/KEventEmitterWrapper.kt +0 -26
  122. package/android/src/main/java/expo/modules/kotlin/methods/Method.kt +0 -14
  123. package/android/src/main/java/expo/modules/kotlin/methods/PromiseMethod.kt +0 -15
  124. package/ios/JSI/JSIInstaller.mm +0 -34
  125. package/ios/JSI/JavaScriptObject.h +0 -60
  126. package/ios/JSI/JavaScriptObject.mm +0 -93
  127. package/ios/JSI/JavaScriptRuntime.mm +0 -102
  128. package/ios/NativeModulesProxy/EXComponentDataCompatibleWrapper.h +0 -16
  129. package/ios/NativeModulesProxy/EXComponentDataCompatibleWrapper.m +0 -28
@@ -0,0 +1,182 @@
1
+ // Copyright 2022-present 650 Industries. All rights reserved.
2
+
3
+ /**
4
+ Represents a function that can only be called asynchronously, thus its JavaScript equivalent returns a Promise.
5
+
6
+ - ToDo: Move some asynchronous logic from `ConcreteFunction` (like `call(args:promise:)`) to this class and drop the `isAsync` property.
7
+ */
8
+ internal final class AsyncFunctionComponent<Args, ReturnType>: ConcreteFunction<Args, ReturnType> {
9
+ override init(
10
+ _ name: String,
11
+ argTypes: [AnyArgumentType],
12
+ _ closure: @escaping ConcreteFunction<Args, ReturnType>.ClosureType
13
+ ) {
14
+ super.init(name, argTypes: argTypes, closure)
15
+ self.isAsync = true
16
+ }
17
+ }
18
+
19
+ /**
20
+ Asynchronous function without arguments.
21
+ */
22
+ public func AsyncFunction<R>(
23
+ _ name: String,
24
+ _ closure: @escaping () throws -> R
25
+ ) -> AnyFunction {
26
+ return AsyncFunctionComponent(
27
+ name,
28
+ argTypes: [],
29
+ closure
30
+ )
31
+ }
32
+
33
+ /**
34
+ Asynchronous function with one argument.
35
+ */
36
+ public func AsyncFunction<R, A0: AnyArgument>(
37
+ _ name: String,
38
+ _ closure: @escaping (A0) throws -> R
39
+ ) -> AnyFunction {
40
+ return AsyncFunctionComponent(
41
+ name,
42
+ argTypes: [ArgumentType(A0.self)],
43
+ closure
44
+ )
45
+ }
46
+
47
+ /**
48
+ Asynchronous function with two arguments.
49
+ */
50
+ public func AsyncFunction<R, A0: AnyArgument, A1: AnyArgument>(
51
+ _ name: String,
52
+ _ closure: @escaping (A0, A1) throws -> R
53
+ ) -> AnyFunction {
54
+ return AsyncFunctionComponent(
55
+ name,
56
+ argTypes: [ArgumentType(A0.self), ArgumentType(A1.self)],
57
+ closure
58
+ )
59
+ }
60
+
61
+ /**
62
+ Asynchronous function with three arguments.
63
+ */
64
+ public func AsyncFunction<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument>(
65
+ _ name: String,
66
+ _ closure: @escaping (A0, A1, A2) throws -> R
67
+ ) -> AnyFunction {
68
+ return AsyncFunctionComponent(
69
+ name,
70
+ argTypes: [
71
+ ArgumentType(A0.self),
72
+ ArgumentType(A1.self),
73
+ ArgumentType(A2.self)
74
+ ],
75
+ closure
76
+ )
77
+ }
78
+
79
+ /**
80
+ Asynchronous function with four arguments.
81
+ */
82
+ public func AsyncFunction<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument>(
83
+ _ name: String,
84
+ _ closure: @escaping (A0, A1, A2, A3) throws -> R
85
+ ) -> AnyFunction {
86
+ return AsyncFunctionComponent(
87
+ name,
88
+ argTypes: [
89
+ ArgumentType(A0.self),
90
+ ArgumentType(A1.self),
91
+ ArgumentType(A2.self),
92
+ ArgumentType(A3.self)
93
+ ],
94
+ closure
95
+ )
96
+ }
97
+
98
+ /**
99
+ Asynchronous function with five arguments.
100
+ */
101
+ public func AsyncFunction<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument>(
102
+ _ name: String,
103
+ _ closure: @escaping (A0, A1, A2, A3, A4) throws -> R
104
+ ) -> AnyFunction {
105
+ return AsyncFunctionComponent(
106
+ name,
107
+ argTypes: [
108
+ ArgumentType(A0.self),
109
+ ArgumentType(A1.self),
110
+ ArgumentType(A2.self),
111
+ ArgumentType(A3.self),
112
+ ArgumentType(A4.self)
113
+ ],
114
+ closure
115
+ )
116
+ }
117
+
118
+ /**
119
+ Asynchronous function with six arguments.
120
+ */
121
+ public func AsyncFunction<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument, A5: AnyArgument>(
122
+ _ name: String,
123
+ _ closure: @escaping (A0, A1, A2, A3, A4, A5) throws -> R
124
+ ) -> AnyFunction {
125
+ return AsyncFunctionComponent(
126
+ name,
127
+ argTypes: [
128
+ ArgumentType(A0.self),
129
+ ArgumentType(A1.self),
130
+ ArgumentType(A2.self),
131
+ ArgumentType(A3.self),
132
+ ArgumentType(A4.self),
133
+ ArgumentType(A5.self)
134
+ ],
135
+ closure
136
+ )
137
+ }
138
+
139
+ /**
140
+ Asynchronous function with seven arguments.
141
+ */
142
+ public func AsyncFunction<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument, A5: AnyArgument, A6: AnyArgument>(
143
+ _ name: String,
144
+ _ closure: @escaping (A0, A1, A2, A3, A4, A5, A6) throws -> R
145
+ ) -> AnyFunction {
146
+ return AsyncFunctionComponent(
147
+ name,
148
+ argTypes: [
149
+ ArgumentType(A0.self),
150
+ ArgumentType(A1.self),
151
+ ArgumentType(A2.self),
152
+ ArgumentType(A3.self),
153
+ ArgumentType(A4.self),
154
+ ArgumentType(A5.self),
155
+ ArgumentType(A6.self)
156
+ ],
157
+ closure
158
+ )
159
+ }
160
+
161
+ /**
162
+ Asynchronous function with eight arguments.
163
+ */
164
+ public func AsyncFunction<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument, A5: AnyArgument, A6: AnyArgument, A7: AnyArgument>(
165
+ _ name: String,
166
+ _ closure: @escaping (A0, A1, A2, A3, A4, A5, A6, A7) throws -> R
167
+ ) -> AnyFunction {
168
+ return AsyncFunctionComponent(
169
+ name,
170
+ argTypes: [
171
+ ArgumentType(A0.self),
172
+ ArgumentType(A1.self),
173
+ ArgumentType(A2.self),
174
+ ArgumentType(A3.self),
175
+ ArgumentType(A4.self),
176
+ ArgumentType(A5.self),
177
+ ArgumentType(A6.self),
178
+ ArgumentType(A7.self)
179
+ ],
180
+ closure
181
+ )
182
+ }
@@ -1,16 +1,14 @@
1
1
  import Dispatch
2
2
 
3
- public final class ConcreteFunction<Args, ReturnType>: AnyFunction {
3
+ public class ConcreteFunction<Args, ReturnType>: AnyFunction {
4
4
  public typealias ClosureType = (Args) throws -> ReturnType
5
5
 
6
6
  public let name: String
7
7
 
8
- public var takesPromise: Bool {
9
- return argTypes.last is PromiseArgumentType
10
- }
8
+ public var takesPromise: Bool
11
9
 
12
10
  public var argumentsCount: Int {
13
- return argTypes.count - (takesPromise ? 1 : 0)
11
+ return argumentTypes.count
14
12
  }
15
13
 
16
14
  public var queue: DispatchQueue?
@@ -19,7 +17,7 @@ public final class ConcreteFunction<Args, ReturnType>: AnyFunction {
19
17
 
20
18
  let closure: ClosureType
21
19
 
22
- let argTypes: [AnyArgumentType]
20
+ public let argumentTypes: [AnyArgumentType]
23
21
 
24
22
  init(
25
23
  _ name: String,
@@ -27,23 +25,33 @@ public final class ConcreteFunction<Args, ReturnType>: AnyFunction {
27
25
  _ closure: @escaping ClosureType
28
26
  ) {
29
27
  self.name = name
30
- self.argTypes = argTypes
28
+ self.takesPromise = argTypes.last is PromiseArgumentType
31
29
  self.closure = closure
30
+
31
+ // Drop the last argument type if it's the `Promise`.
32
+ self.argumentTypes = takesPromise ? argTypes.dropLast(1) : argTypes
33
+
34
+ // This is temporary solution to keep backwards compatibility for existing functions — they all end with "Async".
35
+ // `function` component that we've used so far was async by default, but we decided to replace it with `asyncFunction`
36
+ // and make `function`s synchronous. Introduced in SDK45, can be removed in SDK46 after migrating all modules.
37
+ self.isAsync = name.hasSuffix("Async")
32
38
  }
33
39
 
40
+ /**
41
+ Calls the function with given arguments.
42
+ - Parameters:
43
+ - args: An array of arguments to pass to the function. The arguments must be of the same type as in the underlying ``closure``.
44
+ - promise: A promise to resolve or reject by the async ``closure`` when it finishes execution.
45
+ - ToDo: Make it internal.
46
+ */
34
47
  public func call(args: [Any], promise: Promise) {
35
- let takesPromise = self.takesPromise
48
+ // Add promise to the array of arguments if necessary.
49
+ let arguments = takesPromise ? args + [promise] : args
36
50
  let returnedValue: ReturnType?
37
51
 
38
52
  do {
39
- var finalArgs = try castArguments(args)
40
-
41
- if takesPromise {
42
- finalArgs.append(promise)
43
- }
44
-
45
- let tuple = try Conversions.toTuple(finalArgs) as! Args
46
- returnedValue = try closure(tuple)
53
+ let argumentsTuple = try Conversions.toTuple(arguments) as! Args
54
+ returnedValue = try closure(argumentsTuple)
47
55
  } catch let error as CodedError {
48
56
  promise.reject(FunctionCallException(name).causedBy(error))
49
57
  return
@@ -56,28 +64,24 @@ public final class ConcreteFunction<Args, ReturnType>: AnyFunction {
56
64
  }
57
65
  }
58
66
 
67
+ /**
68
+ Calls the function synchronously with given arguments.
69
+ - Parameters:
70
+ - args: An array of arguments to pass to the function. The arguments must be of the same type as in the underlying ``closure``.
71
+ - Returns: A value returned by the called function when succeeded or an error when it failed.
72
+ - ToDo: Make it internal.
73
+ */
59
74
  public func callSync(args: [Any]) -> Any {
60
75
  if takesPromise {
61
- var result: Any?
62
- let semaphore = DispatchSemaphore(value: 0)
63
-
64
- let promise = Promise {
65
- result = $0
66
- semaphore.signal()
67
- } rejecter: { _ in
68
- semaphore.signal()
69
- }
70
- call(args: args, promise: promise)
71
- semaphore.wait()
72
- return result as Any
73
- } else {
74
- do {
75
- let finalArgs = try castArguments(args)
76
- let tuple = try Conversions.toTuple(finalArgs) as! Args
77
- return try closure(tuple)
78
- } catch let error {
79
- return error
80
- }
76
+ // Using `Promise` in the synchronous function is prohibited. Probably should throw an exception here,
77
+ // but for now let's return nil until we split async and sync functions.
78
+ return Optional<Any>.none as Any
79
+ }
80
+ do {
81
+ let argumentsTuple = try Conversions.toTuple(args) as! Args
82
+ return try closure(argumentsTuple)
83
+ } catch let error {
84
+ return error
81
85
  }
82
86
  }
83
87
 
@@ -90,38 +94,6 @@ public final class ConcreteFunction<Args, ReturnType>: AnyFunction {
90
94
  self.isAsync = false
91
95
  return self
92
96
  }
93
-
94
- private func argumentType(atIndex index: Int) -> AnyArgumentType? {
95
- return (0..<argTypes.count).contains(index) ? argTypes[index] : nil
96
- }
97
-
98
- private func castArguments(_ args: [Any]) throws -> [Any] {
99
- if args.count != argumentsCount {
100
- throw InvalidArgsNumberException((received: args.count, expected: argumentsCount))
101
- }
102
- return try args.enumerated().map { index, arg in
103
- let expectedType = argumentType(atIndex: index)
104
-
105
- do {
106
- // It's safe to unwrap since the arguments count matches.
107
- return try expectedType!.cast(arg)
108
- } catch {
109
- throw ArgumentCastException((index: index, type: expectedType!)).causedBy(error)
110
- }
111
- }
112
- }
113
- }
114
-
115
- internal class InvalidArgsNumberException: GenericException<(received: Int, expected: Int)> {
116
- override var reason: String {
117
- "Received \(param.received) arguments, but \(param.expected) was expected"
118
- }
119
- }
120
-
121
- internal class ArgumentCastException: GenericException<(index: Int, type: AnyArgumentType)> {
122
- override var reason: String {
123
- "Argument at index '\(param.index)' couldn't be cast to type \(param.type.description)"
124
- }
125
97
  }
126
98
 
127
99
  internal class FunctionCallException: GenericException<String> {
@@ -0,0 +1,181 @@
1
+ // Copyright 2022-present 650 Industries. All rights reserved.
2
+
3
+ /**
4
+ Represents a function that can only be called synchronously.
5
+ - ToDo: Move some synchronous logic from `ConcreteFunction` (like `call(args:)`) to this class and drop the `isAsync` property.
6
+ */
7
+ internal final class SyncFunctionComponent<Args, ReturnType>: ConcreteFunction<Args, ReturnType> {
8
+ override init(
9
+ _ name: String,
10
+ argTypes: [AnyArgumentType],
11
+ _ closure: @escaping ConcreteFunction<Args, ReturnType>.ClosureType
12
+ ) {
13
+ super.init(name, argTypes: argTypes, closure)
14
+ self.isAsync = false
15
+ }
16
+ }
17
+
18
+ /**
19
+ Synchronous function without arguments.
20
+ */
21
+ public func Function<R>(
22
+ _ name: String,
23
+ _ closure: @escaping () throws -> R
24
+ ) -> AnyFunction {
25
+ return SyncFunctionComponent(
26
+ name,
27
+ argTypes: [],
28
+ closure
29
+ )
30
+ }
31
+
32
+ /**
33
+ Synchronous function with one argument.
34
+ */
35
+ public func Function<R, A0: AnyArgument>(
36
+ _ name: String,
37
+ _ closure: @escaping (A0) throws -> R
38
+ ) -> AnyFunction {
39
+ return SyncFunctionComponent(
40
+ name,
41
+ argTypes: [ArgumentType(A0.self)],
42
+ closure
43
+ )
44
+ }
45
+
46
+ /**
47
+ Synchronous function with two arguments.
48
+ */
49
+ public func Function<R, A0: AnyArgument, A1: AnyArgument>(
50
+ _ name: String,
51
+ _ closure: @escaping (A0, A1) throws -> R
52
+ ) -> AnyFunction {
53
+ return SyncFunctionComponent(
54
+ name,
55
+ argTypes: [ArgumentType(A0.self), ArgumentType(A1.self)],
56
+ closure
57
+ )
58
+ }
59
+
60
+ /**
61
+ Synchronous function with three arguments.
62
+ */
63
+ public func Function<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument>(
64
+ _ name: String,
65
+ _ closure: @escaping (A0, A1, A2) throws -> R
66
+ ) -> AnyFunction {
67
+ return SyncFunctionComponent(
68
+ name,
69
+ argTypes: [
70
+ ArgumentType(A0.self),
71
+ ArgumentType(A1.self),
72
+ ArgumentType(A2.self)
73
+ ],
74
+ closure
75
+ )
76
+ }
77
+
78
+ /**
79
+ Synchronous function with four arguments.
80
+ */
81
+ public func Function<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument>(
82
+ _ name: String,
83
+ _ closure: @escaping (A0, A1, A2, A3) throws -> R
84
+ ) -> AnyFunction {
85
+ return SyncFunctionComponent(
86
+ name,
87
+ argTypes: [
88
+ ArgumentType(A0.self),
89
+ ArgumentType(A1.self),
90
+ ArgumentType(A2.self),
91
+ ArgumentType(A3.self)
92
+ ],
93
+ closure
94
+ )
95
+ }
96
+
97
+ /**
98
+ Synchronous function with five arguments.
99
+ */
100
+ public func Function<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument>(
101
+ _ name: String,
102
+ _ closure: @escaping (A0, A1, A2, A3, A4) throws -> R
103
+ ) -> AnyFunction {
104
+ return SyncFunctionComponent(
105
+ name,
106
+ argTypes: [
107
+ ArgumentType(A0.self),
108
+ ArgumentType(A1.self),
109
+ ArgumentType(A2.self),
110
+ ArgumentType(A3.self),
111
+ ArgumentType(A4.self)
112
+ ],
113
+ closure
114
+ )
115
+ }
116
+
117
+ /**
118
+ Synchronous function with six arguments.
119
+ */
120
+ public func Function<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument, A5: AnyArgument>(
121
+ _ name: String,
122
+ _ closure: @escaping (A0, A1, A2, A3, A4, A5) throws -> R
123
+ ) -> AnyFunction {
124
+ return SyncFunctionComponent(
125
+ name,
126
+ argTypes: [
127
+ ArgumentType(A0.self),
128
+ ArgumentType(A1.self),
129
+ ArgumentType(A2.self),
130
+ ArgumentType(A3.self),
131
+ ArgumentType(A4.self),
132
+ ArgumentType(A5.self)
133
+ ],
134
+ closure
135
+ )
136
+ }
137
+
138
+ /**
139
+ Synchronous function with seven arguments.
140
+ */
141
+ public func Function<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument, A5: AnyArgument, A6: AnyArgument>(
142
+ _ name: String,
143
+ _ closure: @escaping (A0, A1, A2, A3, A4, A5, A6) throws -> R
144
+ ) -> AnyFunction {
145
+ return SyncFunctionComponent(
146
+ name,
147
+ argTypes: [
148
+ ArgumentType(A0.self),
149
+ ArgumentType(A1.self),
150
+ ArgumentType(A2.self),
151
+ ArgumentType(A3.self),
152
+ ArgumentType(A4.self),
153
+ ArgumentType(A5.self),
154
+ ArgumentType(A6.self)
155
+ ],
156
+ closure
157
+ )
158
+ }
159
+
160
+ /**
161
+ Synchronous function with eight arguments.
162
+ */
163
+ public func Function<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument, A5: AnyArgument, A6: AnyArgument, A7: AnyArgument>(
164
+ _ name: String,
165
+ _ closure: @escaping (A0, A1, A2, A3, A4, A5, A6, A7) throws -> R
166
+ ) -> AnyFunction {
167
+ return SyncFunctionComponent(
168
+ name,
169
+ argTypes: [
170
+ ArgumentType(A0.self),
171
+ ArgumentType(A1.self),
172
+ ArgumentType(A2.self),
173
+ ArgumentType(A3.self),
174
+ ArgumentType(A4.self),
175
+ ArgumentType(A5.self),
176
+ ArgumentType(A6.self),
177
+ ArgumentType(A7.self)
178
+ ],
179
+ closure
180
+ )
181
+ }
@@ -36,6 +36,62 @@ internal func createSyncFunctionBlock(holder: ModuleHolder, name functionName: S
36
36
  }
37
37
  }
38
38
 
39
+ // MARK: - Arguments
40
+
41
+ /**
42
+ Tries to cast given argument to the type that is wrapped by the argument type.
43
+ - Parameters:
44
+ - argument: A value to be cast. If it's a ``JavaScriptValue``, it's first unpacked to the raw value.
45
+ - argumentType: Something that implements ``AnyArgumentType`` and knows how to cast the argument.
46
+ - Returns: A new value converted according to the argument type.
47
+ - Throws: Rethrows various exceptions that could be thrown by the argument type wrappers.
48
+ */
49
+ internal func castArgument(_ argument: Any, toType argumentType: AnyArgumentType) throws -> Any {
50
+ // TODO: Accept JavaScriptValue and JavaScriptObject as argument types.
51
+ if let argument = argument as? JavaScriptValue {
52
+ return try argumentType.cast(argument.getRaw())
53
+ }
54
+ return try argumentType.cast(argument)
55
+ }
56
+
57
+ /**
58
+ Same as ``castArgument(_:argumentType:)`` but for an array of arguments.
59
+ - Parameters:
60
+ - arguments: An array of arguments to be cast.
61
+ - argumentTypes: An array of argument types in the same order as the array of arguments.
62
+ - Returns: An array of arguments after casting. Its size is the same as the input arrays.
63
+ - Throws: ``InvalidArgsNumberException`` when the sizes of arrays passed as parameters are not equal.
64
+ Rethrows exceptions thrown by ``castArgument(_:argumentType:)``.
65
+ */
66
+ internal func castArguments(_ arguments: [Any], toTypes argumentTypes: [AnyArgumentType]) throws -> [Any] {
67
+ if arguments.count != argumentTypes.count {
68
+ throw InvalidArgsNumberException((received: arguments.count, expected: argumentTypes.count))
69
+ }
70
+ return try arguments.enumerated().map { index, argument in
71
+ let argumentType = argumentTypes[index]
72
+
73
+ do {
74
+ return try castArgument(argument, toType: argumentType)
75
+ } catch {
76
+ throw ArgumentCastException((index: index, type: argumentType)).causedBy(error)
77
+ }
78
+ }
79
+ }
80
+
81
+ internal class InvalidArgsNumberException: GenericException<(received: Int, expected: Int)> {
82
+ override var reason: String {
83
+ "Received \(param.received) arguments, but \(param.expected) was expected"
84
+ }
85
+ }
86
+
87
+ internal class ArgumentCastException: GenericException<(index: Int, type: AnyArgumentType)> {
88
+ override var reason: String {
89
+ "Argument at index '\(param.index)' couldn't be cast to type \(param.type.description)"
90
+ }
91
+ }
92
+
93
+ // MARK: - Exceptions
94
+
39
95
  private class ModuleUnavailableException: GenericException<String> {
40
96
  override var reason: String {
41
97
  "Module '\(param)' is no longer available"
@@ -70,8 +70,15 @@ public final class ModuleHolder {
70
70
  }
71
71
  let queue = function.queue ?? DispatchQueue.global(qos: .default)
72
72
 
73
+ // Given arguments can be:
74
+ // - Swift primitives when invoked through the bridge and in unit tests
75
+ // - `JavaScriptValue`s when the function is called through the JSI
76
+ // The latter need to be unpacked to Swift primitives on the JS thread,
77
+ // so we do the casting before the function call is scheduled on the queue.
78
+ let arguments = try castArguments(args, toTypes: function.argumentTypes)
79
+
73
80
  queue.async {
74
- function.call(args: args, promise: promise)
81
+ function.call(args: arguments, promise: promise)
75
82
  }
76
83
  } catch let error as CodedError {
77
84
  promise.reject(error)
@@ -91,10 +98,15 @@ public final class ModuleHolder {
91
98
 
92
99
  @discardableResult
93
100
  func callSync(function functionName: String, args: [Any]) -> Any? {
94
- if let function = definition.functions[functionName] {
95
- return function.callSync(args: args)
101
+ guard let function = definition.functions[functionName] else {
102
+ return nil
103
+ }
104
+ do {
105
+ let arguments = try castArguments(args, toTypes: function.argumentTypes)
106
+ return function.callSync(args: arguments)
107
+ } catch {
108
+ return error
96
109
  }
97
- return nil
98
110
  }
99
111
 
100
112
  // MARK: JavaScript Module Object
@@ -113,7 +125,7 @@ public final class ModuleHolder {
113
125
 
114
126
  // Fill in with constants
115
127
  for (key, value) in getConstants() {
116
- object[key] = value
128
+ object.setProperty(key, value: value)
117
129
  }
118
130
 
119
131
  // Fill in with functions