StorageReference+Combine.swift 10 KB

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