@@ -385,6 +385,30 @@ enum FunctionsConstants {
385385 return URL ( string: " https:// \( region) - \( projectID) .cloudfunctions.net/ \( name) " )
386386 }
387387
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+
388412 func callFunction( at url: URL ,
389413 withObject data: Any ? ,
390414 options: HTTPSCallableOptions ? ,
@@ -413,24 +437,54 @@ enum FunctionsConstants {
413437 timeout: TimeInterval ,
414438 context: FunctionsContext ,
415439 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
421441 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+ )
427449 } catch {
428450 DispatchQueue . main. async {
429451 completion ( . failure( error) )
430452 }
431453 return
432454 }
433455
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+
434488 // Set the headers.
435489 fetcher. setRequestValue ( " application/json " , forHTTPHeaderField: " Content-Type " )
436490 if let authToken = context. authToken {
@@ -462,33 +516,27 @@ enum FunctionsConstants {
462516 fetcher. allowedInsecureSchemes = [ " http " ]
463517 }
464518
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+ }
477521
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 )
482530 }
483531
484- private func responseData ( data: Data ? , error: ( any Error ) ? ) throws -> Data {
532+ private func processedResponseData ( from data: Data ? , error: ( any Error ) ? ) throws -> Data {
485533 // Case 1: `error` is not `nil` -> always throws
486534 if let error = error as NSError ? {
487535 let localError : ( any Error ) ?
488536 if error. domain == kGTMSessionFetcherStatusDomain {
489537 localError = FunctionsError (
490538 httpStatusCode: error. code,
491- body: data,
539+ body: data ?? error . userInfo [ " data " ] as? Data ,
492540 serializer: serializer
493541 )
494542 } else if error. domain == NSURLErrorDomain, error. code == NSURLErrorTimedOut {
0 commit comments