@@ -385,6 +385,30 @@ enum FunctionsConstants {
385
385
return URL ( string: " https:// \( region) - \( projectID) .cloudfunctions.net/ \( name) " )
386
386
}
387
387
388
+ @available ( iOS 13 , macCatalyst 13 , macOS 10 . 15 , tvOS 13 , watchOS 7 , * )
389
+ func callFunction( at url: URL ,
390
+ withObject data: Any ? ,
391
+ options: HTTPSCallableOptions ? ,
392
+ timeout: TimeInterval ) async throws -> HTTPSCallableResult {
393
+ let context = try await contextProvider. context ( options: options)
394
+ let fetcher = try makeFetcher (
395
+ url: url,
396
+ data: data,
397
+ options: options,
398
+ timeout: timeout,
399
+ context: context
400
+ )
401
+
402
+ do {
403
+ let rawData = try await fetcher. beginFetch ( )
404
+ return try callableResultFromResponse ( data: rawData, error: nil )
405
+ } catch {
406
+ // This method always throws when `error` is not `nil`, but ideally,
407
+ // it should be refactored so it looks less confusing.
408
+ return try callableResultFromResponse ( data: nil , error: error)
409
+ }
410
+ }
411
+
388
412
func callFunction( at url: URL ,
389
413
withObject data: Any ? ,
390
414
options: HTTPSCallableOptions ? ,
@@ -413,24 +437,54 @@ enum FunctionsConstants {
413
437
timeout: TimeInterval ,
414
438
context: FunctionsContext ,
415
439
completion: @escaping ( ( Result < HTTPSCallableResult , Error > ) -> Void ) ) {
416
- let request = URLRequest ( url: url,
417
- cachePolicy: . useProtocolCachePolicy,
418
- timeoutInterval: timeout)
419
- let fetcher = fetcherService. fetcher ( with: request)
420
-
440
+ let fetcher : GTMSessionFetcher
421
441
do {
422
- let data = data ?? NSNull ( )
423
- let encoded = try serializer. encode ( data)
424
- let body = [ " data " : encoded]
425
- let payload = try JSONSerialization . data ( withJSONObject: body)
426
- fetcher. bodyData = payload
442
+ fetcher = try makeFetcher (
443
+ url: url,
444
+ data: data,
445
+ options: options,
446
+ timeout: timeout,
447
+ context: context
448
+ )
427
449
} catch {
428
450
DispatchQueue . main. async {
429
451
completion ( . failure( error) )
430
452
}
431
453
return
432
454
}
433
455
456
+ fetcher. beginFetch { [ self ] data, error in
457
+ let result : Result < HTTPSCallableResult , any Error >
458
+ do {
459
+ result = try . success( callableResultFromResponse ( data: data, error: error) )
460
+ } catch {
461
+ result = . failure( error)
462
+ }
463
+
464
+ DispatchQueue . main. async {
465
+ completion ( result)
466
+ }
467
+ }
468
+ }
469
+
470
+ private func makeFetcher( url: URL ,
471
+ data: Any ? ,
472
+ options: HTTPSCallableOptions ? ,
473
+ timeout: TimeInterval ,
474
+ context: FunctionsContext ) throws -> GTMSessionFetcher {
475
+ let request = URLRequest (
476
+ url: url,
477
+ cachePolicy: . useProtocolCachePolicy,
478
+ timeoutInterval: timeout
479
+ )
480
+ let fetcher = fetcherService. fetcher ( with: request)
481
+
482
+ let data = data ?? NSNull ( )
483
+ let encoded = try serializer. encode ( data)
484
+ let body = [ " data " : encoded]
485
+ let payload = try JSONSerialization . data ( withJSONObject: body)
486
+ fetcher. bodyData = payload
487
+
434
488
// Set the headers.
435
489
fetcher. setRequestValue ( " application/json " , forHTTPHeaderField: " Content-Type " )
436
490
if let authToken = context. authToken {
@@ -462,33 +516,27 @@ enum FunctionsConstants {
462
516
fetcher. allowedInsecureSchemes = [ " http " ]
463
517
}
464
518
465
- fetcher. beginFetch { [ self ] data, error in
466
- let result : Result < HTTPSCallableResult , any Error >
467
- do {
468
- let data = try responseData ( data: data, error: error)
469
- let json = try responseDataJSON ( from: data)
470
- // TODO: Refactor `decode(_:)` so it either returns a non-optional object or throws
471
- let payload = try serializer. decode ( json)
472
- // TODO: Remove `as Any` once `decode(_:)` is refactored
473
- result = . success( HTTPSCallableResult ( data: payload as Any ) )
474
- } catch {
475
- result = . failure( error)
476
- }
519
+ return fetcher
520
+ }
477
521
478
- DispatchQueue . main. async {
479
- completion ( result)
480
- }
481
- }
522
+ private func callableResultFromResponse( data: Data ? ,
523
+ error: ( any Error ) ? ) throws -> HTTPSCallableResult {
524
+ let processedData = try processedResponseData ( from: data, error: error)
525
+ let json = try responseDataJSON ( from: processedData)
526
+ // TODO: Refactor `decode(_:)` so it either returns a non-optional object or throws
527
+ let payload = try serializer. decode ( json)
528
+ // TODO: Remove `as Any` once `decode(_:)` is refactored
529
+ return HTTPSCallableResult ( data: payload as Any )
482
530
}
483
531
484
- private func responseData ( data: Data ? , error: ( any Error ) ? ) throws -> Data {
532
+ private func processedResponseData ( from data: Data ? , error: ( any Error ) ? ) throws -> Data {
485
533
// Case 1: `error` is not `nil` -> always throws
486
534
if let error = error as NSError ? {
487
535
let localError : ( any Error ) ?
488
536
if error. domain == kGTMSessionFetcherStatusDomain {
489
537
localError = FunctionsError (
490
538
httpStatusCode: error. code,
491
- body: data,
539
+ body: data ?? error . userInfo [ " data " ] as? Data ,
492
540
serializer: serializer
493
541
)
494
542
} else if error. domain == NSURLErrorDomain, error. code == NSURLErrorTimedOut {
0 commit comments