Callable+Codable.swift 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. // Copyright 2021 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. import Foundation
  15. import FirebaseFunctions
  16. import FirebaseSharedSwift
  17. public extension Functions {
  18. /// Creates a reference to the Callable HTTPS trigger with the given name, the type of an `Encodable`
  19. /// request and the type of a `Decodable` response.
  20. /// - Parameter name: The name of the Callable HTTPS trigger
  21. /// - Parameter requestAs: The type of the `Encodable` entity to use for requests to this `Callable`
  22. /// - Parameter responseAs: The type of the `Decodable` entity to use for responses from this `Callable`
  23. /// - Parameter encoder: The encoder instance to use to run the encoding.
  24. /// - Parameter decoder: The decoder instance to use to run the decoding.
  25. func httpsCallable<Request: Encodable,
  26. Response: Decodable>(_ name: String,
  27. requestAs: Request.Type = Request.self,
  28. responseAs: Response.Type = Response.self,
  29. encoder: FirebaseDataEncoder = FirebaseDataEncoder(),
  30. decoder: FirebaseDataDecoder = FirebaseDataDecoder())
  31. -> Callable<Request, Response> {
  32. return Callable(callable: httpsCallable(name), encoder: encoder, decoder: decoder)
  33. }
  34. }
  35. // A `Callable` is reference to a particular Callable HTTPS trigger in Cloud Functions.
  36. public struct Callable<Request: Encodable, Response: Decodable> {
  37. /// The timeout to use when calling the function. Defaults to 60 seconds.
  38. public var timeoutInterval: TimeInterval {
  39. get {
  40. callable.timeoutInterval
  41. }
  42. set {
  43. callable.timeoutInterval = newValue
  44. }
  45. }
  46. enum CallableError: Error {
  47. case internalError
  48. }
  49. private let callable: HTTPSCallable
  50. private let encoder: FirebaseDataEncoder
  51. private let decoder: FirebaseDataDecoder
  52. init(callable: HTTPSCallable, encoder: FirebaseDataEncoder, decoder: FirebaseDataDecoder) {
  53. self.callable = callable
  54. self.encoder = encoder
  55. self.decoder = decoder
  56. }
  57. /// Executes this Callable HTTPS trigger asynchronously.
  58. ///
  59. /// The data passed into the trigger must be of the generic `Request` type:
  60. ///
  61. /// The request to the Cloud Functions backend made by this method automatically includes a
  62. /// FCM token to identify the app instance. If a user is logged in with Firebase
  63. /// Auth, an auth ID token for the user is also automatically included.
  64. ///
  65. /// Firebase Cloud Messaging sends data to the Firebase backend periodically to collect information
  66. /// regarding the app instance. To stop this, see `Messaging.deleteData()`. It
  67. /// resumes with a new FCM Token the next time you call this method.
  68. ///
  69. /// - Parameter data: Parameters to pass to the trigger.
  70. /// - Parameter completion: The block to call when the HTTPS request has completed.
  71. public func call(_ data: Request,
  72. completion: @escaping (Result<Response, Error>)
  73. -> Void) {
  74. do {
  75. let encoded = try encoder.encode(data)
  76. callable.call(encoded) { result, error in
  77. do {
  78. if let result = result {
  79. let decoded = try decoder.decode(Response.self, from: result.data)
  80. completion(.success(decoded))
  81. } else if let error = error {
  82. completion(.failure(error))
  83. } else {
  84. completion(.failure(CallableError.internalError))
  85. }
  86. } catch {
  87. completion(.failure(error))
  88. }
  89. }
  90. } catch {
  91. completion(.failure(error))
  92. }
  93. }
  94. /// Creates a directly callable function.
  95. ///
  96. /// This allows users to call a HTTPS Callable Function like a normal Swift function:
  97. /// ```swift
  98. /// let greeter = functions.httpsCallable("greeter",
  99. /// requestType: GreetingRequest.self,
  100. /// responseType: GreetingResponse.self)
  101. /// greeter(data) { result in
  102. /// print(result.greeting)
  103. /// }
  104. /// ```
  105. /// You can also call a HTTPS Callable function using the following syntax:
  106. /// ```swift
  107. /// let greeter: Callable<GreetingRequest, GreetingResponse> = functions.httpsCallable("greeter")
  108. /// greeter(data) { result in
  109. /// print(result.greeting)
  110. /// }
  111. /// ```
  112. /// - Parameters:
  113. /// - data: Parameters to pass to the trigger.
  114. /// - completion: The block to call when the HTTPS request has completed.
  115. public func callAsFunction(_ data: Request,
  116. completion: @escaping (Result<Response, Error>)
  117. -> Void) {
  118. call(data, completion: completion)
  119. }
  120. #if compiler(>=5.5) && canImport(_Concurrency)
  121. /// Executes this Callable HTTPS trigger asynchronously.
  122. ///
  123. /// The data passed into the trigger must be of the generic `Request` type:
  124. ///
  125. /// The request to the Cloud Functions backend made by this method automatically includes a
  126. /// FCM token to identify the app instance. If a user is logged in with Firebase
  127. /// Auth, an auth ID token for the user is also automatically included.
  128. ///
  129. /// Firebase Cloud Messaging sends data to the Firebase backend periodically to collect information
  130. /// regarding the app instance. To stop this, see `Messaging.deleteData()`. It
  131. /// resumes with a new FCM Token the next time you call this method.
  132. ///
  133. /// - Parameter data: The `Request` representing the data to pass to the trigger.
  134. /// - Parameter encoder: The encoder instance to use to run the encoding.
  135. /// - Parameter decoder: The decoder instance to use to run the decoding.
  136. ///
  137. /// - Throws: An error if any value throws an error during encoding or decoding.
  138. /// - Throws: An error if the callable fails to complete
  139. ///
  140. /// - Returns: The decoded `Response` value
  141. @available(iOS 15, tvOS 15, macOS 12, watchOS 8, *)
  142. public func call(_ data: Request,
  143. encoder: FirebaseDataEncoder = FirebaseDataEncoder(),
  144. decoder: FirebaseDataDecoder =
  145. FirebaseDataDecoder()) async throws -> Response {
  146. let encoded = try encoder.encode(data)
  147. let result = try await callable.call(encoded)
  148. return try decoder.decode(Response.self, from: result.data)
  149. }
  150. /// Creates a directly callable function.
  151. ///
  152. /// This allows users to call a HTTPS Callable Function like a normal Swift function:
  153. /// ```swift
  154. /// let greeter = functions.httpsCallable("greeter",
  155. /// requestType: GreetingRequest.self,
  156. /// responseType: GreetingResponse.self)
  157. /// let result = try await greeter(data)
  158. /// print(result.greeting)
  159. /// ```
  160. /// You can also call a HTTPS Callable function using the following syntax:
  161. /// ```swift
  162. /// let greeter: Callable<GreetingRequest, GreetingResponse> = functions.httpsCallable("greeter")
  163. /// let result = try await greeter(data)
  164. /// print(result.greeting)
  165. /// ```
  166. /// - Parameters:
  167. /// - data: Parameters to pass to the trigger.
  168. /// - Returns: The decoded `Response` value
  169. @available(iOS 15, tvOS 15, macOS 12, watchOS 8, *)
  170. public func callAsFunction(_ data: Request) async throws -> Response {
  171. return try await call(data)
  172. }
  173. #endif
  174. }