Callable+Codable.swift 7.5 KB

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