1 // Copyright 2022-present 650 Industries. All rights reserved. 2 3 /** 4 Represents a concurrent function that can only be called asynchronously, thus its JavaScript equivalent returns a Promise. 5 As opposed to `AsyncFunctionComponent`, it can leverage the new Swift's concurrency model and take the async/await closure. 6 */ 7 public final class ConcurrentFunctionDefinition<Args, FirstArgType, ReturnType>: AnyFunction { 8 typealias ClosureType = (Args) async throws -> ReturnType 9 10 let body: ClosureType 11 12 init( 13 _ name: String, 14 firstArgType: FirstArgType.Type, 15 dynamicArgumentTypes: [AnyDynamicType], 16 _ body: @escaping ClosureType 17 ) { 18 self.name = name 19 self.body = body 20 self.dynamicArgumentTypes = dynamicArgumentTypes 21 } 22 23 // MARK: - AnyFunction 24 25 let name: String 26 27 let dynamicArgumentTypes: [AnyDynamicType] 28 29 var takesOwner: Bool = false 30 31 func call(by owner: AnyObject?, withArguments args: [Any], appContext: AppContext, callback: @escaping (FunctionCallResult) -> Void) { 32 var arguments: [Any] 33 34 do { 35 try validateArgumentsNumber(function: self, received: args.count) 36 37 arguments = concat( 38 arguments: args, 39 withOwner: owner, 40 withPromise: nil, 41 forFunction: self, 42 appContext: appContext 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) 47 } catch let error as Exception { 48 callback(.failure(error)) 49 return 50 } catch { 51 callback(.failure(UnexpectedException(error))) 52 return 53 } 54 55 // Switch from the synchronous context to asynchronous 56 Task { [arguments] in 57 let result: Result<Any, Exception> 58 59 do { 60 // Convert arguments to the types desired by the function. 61 let finalArguments = try cast(arguments: arguments, forFunction: self, appContext: appContext) 62 63 // TODO: Right now we force cast the tuple in all types of functions, but we should throw another exception here. 64 // swiftlint:disable force_cast 65 let argumentsTuple = try Conversions.toTuple(finalArguments) as! Args 66 let returnValue = try await body(argumentsTuple) 67 68 result = .success(returnValue) 69 } catch let error as Exception { 70 result = .failure(FunctionCallException(name).causedBy(error)) 71 } catch { 72 result = .failure(UnexpectedException(error)) 73 } 74 75 callback(result) 76 } 77 } 78 79 // MARK: - JavaScriptObjectBuilder 80 buildnull81 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 { 90 let exception = NativeFunctionUnavailableException(name) 91 return reject(exception.code, exception.description, nil) 92 } 93 self.call(by: this, withArguments: args, appContext: appContext) { result in 94 switch result { 95 case .failure(let error): 96 reject(error.code, error.description, nil) 97 case .success(let value): 98 resolve(value) 99 } 100 } 101 } 102 } 103 } 104 105 /** 106 Concurrently-executing asynchronous function without arguments. 107 */ 108 public func AsyncFunction<R>( 109 _ name: String, 110 @_implicitSelfCapture _ closure: @escaping () async throws -> R 111 ) -> ConcurrentFunctionDefinition<(), Void, R> { 112 return ConcurrentFunctionDefinition( 113 name, 114 firstArgType: Void.self, 115 dynamicArgumentTypes: [], 116 closure 117 ) 118 } 119 120 /** 121 Concurrently-executing asynchronous function with one argument. 122 */ 123 public func AsyncFunction<R, A0: AnyArgument>( 124 _ name: String, 125 @_implicitSelfCapture _ closure: @escaping (A0) async throws -> R 126 ) -> ConcurrentFunctionDefinition<(A0), A0, R> { 127 return ConcurrentFunctionDefinition( 128 name, 129 firstArgType: A0.self, 130 dynamicArgumentTypes: [~A0.self], 131 closure 132 ) 133 } 134 135 /** 136 Concurrently-executing asynchronous function with two arguments. 137 */ 138 public func AsyncFunction<R, A0: AnyArgument, A1: AnyArgument>( 139 _ name: String, 140 @_implicitSelfCapture _ closure: @escaping (A0, A1) async throws -> R 141 ) -> ConcurrentFunctionDefinition<(A0, A1), A0, R> { 142 return ConcurrentFunctionDefinition( 143 name, 144 firstArgType: A0.self, 145 dynamicArgumentTypes: [~A0.self, ~A1.self], 146 closure 147 ) 148 } 149 150 /** 151 Concurrently-executing asynchronous function with three arguments. 152 */ 153 public func AsyncFunction<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument>( 154 _ name: String, 155 @_implicitSelfCapture _ closure: @escaping (A0, A1, A2) async throws -> R 156 ) -> ConcurrentFunctionDefinition<(A0, A1, A2), A0, R> { 157 return ConcurrentFunctionDefinition( 158 name, 159 firstArgType: A0.self, 160 dynamicArgumentTypes: [ 161 ~A0.self, 162 ~A1.self, 163 ~A2.self 164 ], 165 closure 166 ) 167 } 168 169 /** 170 Concurrently-executing asynchronous function with four arguments. 171 */ 172 public func AsyncFunction<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument>( 173 _ name: String, 174 @_implicitSelfCapture _ closure: @escaping (A0, A1, A2, A3) async throws -> R 175 ) -> ConcurrentFunctionDefinition<(A0, A1, A2, A3), A0, R> { 176 return ConcurrentFunctionDefinition( 177 name, 178 firstArgType: A0.self, 179 dynamicArgumentTypes: [ 180 ~A0.self, 181 ~A1.self, 182 ~A2.self, 183 ~A3.self 184 ], 185 closure 186 ) 187 } 188 189 /** 190 Concurrently-executing asynchronous function with five arguments. 191 */ 192 public func AsyncFunction<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument>( 193 _ name: String, 194 @_implicitSelfCapture _ closure: @escaping (A0, A1, A2, A3, A4) async throws -> R 195 ) -> ConcurrentFunctionDefinition<(A0, A1, A2, A3, A4), A0, R> { 196 return ConcurrentFunctionDefinition( 197 name, 198 firstArgType: A0.self, 199 dynamicArgumentTypes: [ 200 ~A0.self, 201 ~A1.self, 202 ~A2.self, 203 ~A3.self, 204 ~A4.self 205 ], 206 closure 207 ) 208 } 209 210 /** 211 Concurrently-executing asynchronous function with six arguments. 212 */ 213 public func AsyncFunction<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument, A5: AnyArgument>( 214 _ name: String, 215 @_implicitSelfCapture _ closure: @escaping (A0, A1, A2, A3, A4, A5) async throws -> R 216 ) -> ConcurrentFunctionDefinition<(A0, A1, A2, A3, A4, A5), A0, R> { 217 return ConcurrentFunctionDefinition( 218 name, 219 firstArgType: A0.self, 220 dynamicArgumentTypes: [ 221 ~A0.self, 222 ~A1.self, 223 ~A2.self, 224 ~A3.self, 225 ~A4.self, 226 ~A5.self 227 ], 228 closure 229 ) 230 } 231 232 /** 233 Concurrently-executing asynchronous function with seven arguments. 234 */ 235 public func AsyncFunction<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument, A5: AnyArgument, A6: AnyArgument>( 236 _ name: String, 237 @_implicitSelfCapture _ closure: @escaping (A0, A1, A2, A3, A4, A5, A6) async throws -> R 238 ) -> ConcurrentFunctionDefinition<(A0, A1, A2, A3, A4, A5, A6), A0, R> { 239 return ConcurrentFunctionDefinition( 240 name, 241 firstArgType: A0.self, 242 dynamicArgumentTypes: [ 243 ~A0.self, 244 ~A1.self, 245 ~A2.self, 246 ~A3.self, 247 ~A4.self, 248 ~A5.self, 249 ~A6.self 250 ], 251 closure 252 ) 253 } 254 255 /** 256 Concurrently-executing asynchronous function with eight arguments. 257 */ 258 public func AsyncFunction< 259 R, 260 A0: AnyArgument, 261 A1: AnyArgument, 262 A2: AnyArgument, 263 A3: AnyArgument, 264 A4: AnyArgument, 265 A5: AnyArgument, 266 A6: AnyArgument, 267 A7: AnyArgument 268 >( 269 _ name: String, 270 @_implicitSelfCapture _ closure: @escaping (A0, A1, A2, A3, A4, A5, A6, A7) async throws -> R 271 ) -> ConcurrentFunctionDefinition<(A0, A1, A2, A3, A4, A5, A6, A7), A0, R> { 272 return ConcurrentFunctionDefinition( 273 name, 274 firstArgType: A0.self, 275 dynamicArgumentTypes: [ 276 ~A0.self, 277 ~A1.self, 278 ~A2.self, 279 ~A3.self, 280 ~A4.self, 281 ~A5.self, 282 ~A6.self, 283 ~A7.self 284 ], 285 closure 286 ) 287 } 288