StorageReference+Combine.swift 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  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. #if canImport(Combine) && swift(>=5.0)
  15. import Combine
  16. import FirebaseStorage
  17. import FirebaseStorageSwift
  18. @available(swift 5.0)
  19. @available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 6.0, *)
  20. extension StorageReference {
  21. // MARK: - Uploads
  22. /// Asynchronously uploads data to the currently specified `StorageReference`.
  23. /// This is not recommended for large files, and one should instead upload a file from disk.
  24. ///
  25. /// The publisher will emit events on the **main** thread.
  26. ///
  27. /// - Parameters:
  28. /// - uploadData: The Data to upload.
  29. /// - metadata: metadata `StorageMetadata` containing additional information (MIME type, etc.)
  30. ///
  31. /// - Returns: A publisher emitting a `StorageMetadata` instance. The publisher will emit on the *main* thread.
  32. @discardableResult
  33. public func putData(_ data: Data,
  34. metadata: StorageMetadata? = nil) -> Future<StorageMetadata, Error> {
  35. var task: StorageUploadTask?
  36. return Future<StorageMetadata, Error> { promise in
  37. task = self.putData(data, metadata: metadata) { result in
  38. promise(result)
  39. }
  40. }
  41. .handleEvents(receiveCancel: {
  42. task?.cancel()
  43. })
  44. .upstream
  45. }
  46. /// Asynchronously uploads a file to the currently specified `StorageReference`.
  47. ///
  48. /// The publisher will emit events on the **main** thread.
  49. ///
  50. /// - Parameters:
  51. /// - fileURL: A `URL` representing the system file path of the object to be uploaded.
  52. /// - metadata: `StorageMetadata` containing additional information (MIME type, etc.) about the object being uploaded.
  53. ///
  54. /// - Returns: A publisher emitting a `StorageMetadata` instance. The publisher will emit on the *main* thread.
  55. @discardableResult
  56. public func putFile(from fileURL: URL,
  57. metadata: StorageMetadata? = nil)
  58. -> Future<StorageMetadata, Error> {
  59. var task: StorageUploadTask?
  60. return Future<StorageMetadata, Error> { promise in
  61. task = self.putFile(from: fileURL, metadata: metadata) { result in
  62. promise(result)
  63. }
  64. }
  65. .handleEvents(receiveCancel: {
  66. task?.cancel()
  67. })
  68. .upstream
  69. }
  70. // MARK: - Downloads
  71. /// Asynchronously downloads the object at the `StorageReference` to an `Data` object in memory.
  72. /// An `Data` of the provided max size will be allocated, so ensure that the device has enough free
  73. /// memory to complete the download. For downloading large files, `writeToFile` may be a better option.
  74. ///
  75. /// The publisher will emit events on the **main** thread.
  76. ///
  77. /// - Parameters:
  78. /// - size: The maximum size in bytes to download. If the download exceeds this size,
  79. /// the task will be cancelled and an error will be returned.
  80. ///
  81. /// - Returns: A publisher emitting a `Data` instance. The publisher will emit on the *main* thread.
  82. @discardableResult
  83. public func getData(maxSize size: Int64) -> Future<Data, Error> {
  84. var task: StorageDownloadTask?
  85. return Future<Data, Error> { promise in
  86. task = self.getData(maxSize: size) { result in
  87. promise(result)
  88. }
  89. }
  90. .handleEvents(receiveCancel: {
  91. task?.cancel()
  92. })
  93. .upstream
  94. }
  95. /// Asynchronously downloads the object at the current path to a specified system filepath.
  96. ///
  97. /// The publisher will emit events on the **main** thread.
  98. ///
  99. /// - Parameters:
  100. /// - fileURL: A file system URL representing the path the object should be downloaded to.
  101. ///
  102. /// - Returns: A publisher emitting a `URL` pointing to the file path of the downloaded file
  103. /// on success. The publisher will emit on the *main* thread.
  104. @discardableResult
  105. public func write(toFile fileURL: URL) -> Future<URL, Error> {
  106. var task: StorageDownloadTask?
  107. return Future<URL, Error> { promise in
  108. task = self.write(toFile: fileURL) { result in
  109. promise(result)
  110. }
  111. }
  112. .handleEvents(receiveCancel: {
  113. task?.cancel()
  114. })
  115. .upstream
  116. }
  117. /// Asynchronously retrieves a long lived download URL with a revokable token.
  118. /// This can be used to share the file with others, but can be revoked by a developer
  119. /// in the Firebase Console if desired.
  120. ///
  121. /// The publisher will emit events on the **main** thread.
  122. ///
  123. /// - Returns: A publisher emitting a `URL` instance. The publisher will emit on the *main* thread.
  124. @discardableResult
  125. public func downloadURL() -> Future<URL, Error> {
  126. Future<URL, Error> { promise in
  127. self.downloadURL { result in
  128. promise(result)
  129. }
  130. }
  131. }
  132. // MARK: - List Support
  133. /// List all items (files) and prefixes (folders) under this `StorageReference`.
  134. ///
  135. /// This is a helper method for calling `list()` repeatedly until there are no more results.
  136. /// Consistency of the result is not guaranteed if objects are inserted or removed while this
  137. /// operation is executing. All results are buffered in memory.
  138. ///
  139. /// The publisher will emit events on the **main** thread.
  140. ///
  141. /// - Remark:
  142. /// `listAll` is only available for projects using Firebase Rules Version 2.
  143. ///
  144. /// - Returns: A publisher emitting a `StorageListResult` instance. The publisher will emit on the *main* thread.
  145. @discardableResult
  146. public func listAll() -> Future<StorageListResult, Error> {
  147. Future<StorageListResult, Error> { promise in
  148. self.listAll { result in
  149. promise(result)
  150. }
  151. }
  152. }
  153. /// List up to `maxResults` items (files) and prefixes (folders) under this `StorageReference`.
  154. ///
  155. /// Note that "/" is treated as a path delimiter. Firebase Storage does not support unsupported object
  156. /// paths that end with "/" or contain two consecutive "/"s. All invalid objects in Firebase Storage will be
  157. /// filtered.
  158. ///
  159. /// The publisher will emit events on the **main** thread.
  160. ///
  161. /// - Parameters:
  162. /// - maxResults: The maximum number of results to return in a single page. Must be greater
  163. /// than 0 and at most 1000.
  164. ///
  165. /// - Remark:
  166. /// `list(maxResults:)` is only available for projects using Firebase Rules Version 2.
  167. ///
  168. /// - Returns: A publisher emitting a `StorageListResult` instance. The publisher will emit on the *main* thread.
  169. @discardableResult
  170. public func list(maxResults: Int64) -> Future<StorageListResult, Error> {
  171. Future<StorageListResult, Error> { promise in
  172. self.list(maxResults: maxResults) { result in
  173. promise(result)
  174. }
  175. }
  176. }
  177. /// Resumes a previous call to `list(maxResults:)`, starting after a pagination token.
  178. /// Returns the next set of items (files) and prefixes (folders) under this `StorageReference.
  179. ///
  180. /// Note that "/" is treated as a path delimiter. Firebase Storage does not support unsupported object
  181. /// paths that end with "/" or contain two consecutive "/"s. All invalid objects in Firebase Storage
  182. /// will be filtered out.
  183. ///
  184. /// The publisher will emit events on the **main** thread.
  185. ///
  186. /// - Parameters:
  187. /// - maxResults: The maximum number of results to return in a single page. Must be greater
  188. /// than 0 and at most 1000.
  189. /// - pageToken: A page token from a previous call to list.
  190. ///
  191. /// - Remark:
  192. /// `list(maxResults:pageToken:)` is only available for projects using Firebase Rules Version 2.
  193. ///
  194. /// - Returns: A publisher emitting a `StorageListResult` instance. The publisher will emit on the *main* thread.
  195. @discardableResult
  196. public func list(maxResults: Int64, pageToken: String) -> Future<StorageListResult, Error> {
  197. Future<StorageListResult, Error> { promise in
  198. self.list(maxResults: maxResults, pageToken: pageToken) { result in
  199. promise(result)
  200. }
  201. }
  202. }
  203. // MARK: - Metadata Operations
  204. /// Retrieves metadata associated with an object at the current path.
  205. ///
  206. /// The publisher will emit events on the **main** thread.
  207. ///
  208. /// - Returns: A publisher emitting a `StorageMetadata` instance. The publisher will emit on the *main* thread.
  209. @discardableResult
  210. public func getMetadata() -> Future<StorageMetadata, Error> {
  211. Future<StorageMetadata, Error> { promise in
  212. self.getMetadata { result in
  213. promise(result)
  214. }
  215. }
  216. }
  217. /// Updates the metadata associated with an object at the current path.
  218. ///
  219. /// The publisher will emit events on the **main** thread.
  220. ///
  221. /// - Parameters:
  222. /// - metadata: A `StorageMetadata` object with the metadata to update.
  223. ///
  224. /// - Returns: A publisher emitting a `StorageMetadata` instance. The publisher will emit on the *main* thread.
  225. @discardableResult
  226. public func updateMetadata(_ metadata: StorageMetadata) -> Future<StorageMetadata, Error> {
  227. Future<StorageMetadata, Error> { promise in
  228. self.updateMetadata(metadata) { result in
  229. promise(result)
  230. }
  231. }
  232. }
  233. // MARK: - Delete
  234. /// Deletes the object at the current path.
  235. ///
  236. /// The publisher will emit events on the **main** thread.
  237. ///
  238. /// - Returns: A publisher that emits whether the call was successful or not. The publisher will emit on the *main* thread.
  239. @discardableResult
  240. public func delete() -> Future<Bool, Error> {
  241. Future<Bool, Error> { promise in
  242. self.delete { error in
  243. if let error = error {
  244. promise(.failure(error))
  245. } else {
  246. promise(.success(true))
  247. }
  248. }
  249. }
  250. }
  251. }
  252. #endif // canImport(Combine) && swift(>=5.0)