StorageReference.swift 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  1. // Copyright 2022 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. /**
  16. * `StorageReference` represents a reference to a Google Cloud Storage object. Developers can
  17. * upload and download objects, as well as get/set object metadata, and delete an object at the
  18. * path. See the Cloud docs for more details: https://cloud.google.com/storage/
  19. */
  20. @objc(FIRStorageReference) open class StorageReference: NSObject {
  21. // MARK: - Public APIs
  22. /**
  23. * The `Storage` service object which created this reference.
  24. */
  25. @objc public let storage: Storage
  26. /**
  27. * The name of the Google Cloud Storage bucket associated with this reference.
  28. * For example, in `gs://bucket/path/to/object.txt`, the bucket would be 'bucket'.
  29. */
  30. @objc public var bucket: String {
  31. return path.bucket
  32. }
  33. /**
  34. * The full path to this object, not including the Google Cloud Storage bucket.
  35. * In `gs://bucket/path/to/object.txt`, the full path would be: `path/to/object.txt`
  36. */
  37. @objc public var fullPath: String {
  38. return path.object ?? ""
  39. }
  40. /**
  41. * The short name of the object associated with this reference.
  42. * In `gs://bucket/path/to/object.txt`, the name of the object would be `object.txt`.
  43. */
  44. @objc public var name: String {
  45. return (path.object as? NSString)?.lastPathComponent ?? ""
  46. }
  47. /**
  48. * Creates a new `StorageReference` pointing to the root object.
  49. * - Returns: A new `StorageReference` pointing to the root object.
  50. */
  51. @objc open func root() -> StorageReference {
  52. return StorageReference(storage: storage, path: path.root())
  53. }
  54. /**
  55. * Creates a new `StorageReference` pointing to the parent of the current reference
  56. * or `nil` if this instance references the root location.
  57. * For example:
  58. * path = foo/bar/baz parent = foo/bar
  59. * path = foo parent = (root)
  60. * path = (root) parent = nil
  61. * - Returns: A new `StorageReference` pointing to the parent of the current reference.
  62. */
  63. @objc open func parent() -> StorageReference? {
  64. guard let parentPath = path.parent() else {
  65. return nil
  66. }
  67. return StorageReference(storage: storage, path: parentPath)
  68. }
  69. /**
  70. * Creates a new `StorageReference` pointing to a child object of the current reference.
  71. * path = foo child = bar newPath = foo/bar
  72. * path = foo/bar child = baz ntask.impl.snapshotwPath = foo/bar/baz
  73. * All leading and trailing slashes will be removed, and consecutive slashes will be
  74. * compressed to single slashes. For example:
  75. * child = /foo/bar newPath = foo/bar
  76. * child = foo/bar/ newPath = foo/bar
  77. * child = foo///bar newPath = foo/bar
  78. * - Parameter path The path to append to the current path.
  79. * - Returns: A new `StorageReference` pointing to a child location of the current reference.
  80. */
  81. @objc(child:) open func child(_ path: String) -> StorageReference {
  82. return StorageReference(storage: storage, path: self.path.child(path))
  83. }
  84. // MARK: - Uploads
  85. /**
  86. * Asynchronously uploads data to the currently specified `StorageReference`,
  87. * without additional metadata.
  88. * This is not recommended for large files, and one should instead upload a file from disk.
  89. * - Parameters:
  90. * - uploadData: The data to upload.
  91. * - metadata: `StorageMetadata` containing additional information (MIME type, etc.)
  92. * about the object being uploaded.
  93. * - Returns: An instance of `StorageUploadTask`, which can be used to monitor or manage the upload.
  94. */
  95. @objc(putData:metadata:)
  96. @discardableResult
  97. open func putData(_ uploadData: Data, metadata: StorageMetadata? = nil) -> StorageUploadTask {
  98. return putData(uploadData, metadata: metadata, completion: nil)
  99. }
  100. /**
  101. * Asynchronously uploads data to the currently specified `StorageReference`.
  102. * This is not recommended for large files, and one should instead upload a file from disk.
  103. * - Parameter uploadData The data to upload.
  104. * - Returns: An instance of `StorageUploadTask`, which can be used to monitor or manage the upload.
  105. */
  106. @objc(putData:) @discardableResult open func __putData(_ uploadData: Data) -> StorageUploadTask {
  107. return putData(uploadData, metadata: nil, completion: nil)
  108. }
  109. /**
  110. * Asynchronously uploads data to the currently specified `StorageReference`.
  111. * This is not recommended for large files, and one should instead upload a file from disk.
  112. * - Parameters:
  113. * - uploadData: The data to upload.
  114. * - metadata: `StorageMetadata` containing additional information (MIME type, etc.)
  115. * about the object being uploaded.
  116. * - completion: A closure that either returns the object metadata on success,
  117. * or an error on failure.
  118. * - Returns: An instance of `StorageUploadTask`, which can be used to monitor or manage the upload.
  119. */
  120. @objc(putData:metadata:completion:) @discardableResult
  121. open func putData(_ uploadData: Data,
  122. metadata: StorageMetadata? = nil,
  123. completion: ((_: StorageMetadata?, _: Error?) -> Void)?) -> StorageUploadTask {
  124. let putMetadata = metadata ?? StorageMetadata()
  125. if let path = path.object {
  126. putMetadata.path = path
  127. putMetadata.name = (path as NSString).lastPathComponent as String
  128. }
  129. let task = StorageUploadTask(reference: self,
  130. service: storage.fetcherServiceForApp,
  131. queue: storage.dispatchQueue,
  132. data: uploadData,
  133. metadata: putMetadata)
  134. startAndObserveUploadTask(task: task, completion: completion)
  135. return task
  136. }
  137. /**
  138. * Asynchronously uploads a file to the currently specified `StorageReference`.
  139. * `putData` should be used instead of `putFile` in Extensions.
  140. * - Parameters:
  141. * - fileURL: A URL representing the system file path of the object to be uploaded.
  142. * - metadata: `StorageMetadata` containing additional information (MIME type, etc.)
  143. * about the object being uploaded.
  144. * - Returns: An instance of `StorageUploadTask`, which can be used to monitor or manage the upload.
  145. */
  146. @objc(putFile:metadata:) @discardableResult
  147. open func putFile(from fileURL: URL, metadata: StorageMetadata? = nil) -> StorageUploadTask {
  148. return putFile(from: fileURL, metadata: metadata, completion: nil)
  149. }
  150. /**
  151. * Asynchronously uploads a file to the currently specified `StorageReference`,
  152. * without additional metadata.
  153. * `putData` should be used instead of `putFile` in Extensions.
  154. * @param fileURL A URL representing the system file path of the object to be uploaded.
  155. * @return An instance of StorageUploadTask, which can be used to monitor or manage the upload.
  156. */
  157. @objc(putFile:) @discardableResult open func __putFile(from fileURL: URL) -> StorageUploadTask {
  158. return putFile(from: fileURL, metadata: nil, completion: nil)
  159. }
  160. /**
  161. * Asynchronously uploads a file to the currently specified `StorageReference`.
  162. * `putData` should be used instead of `putFile` in Extensions.
  163. * - Parameters:
  164. * - fileURL: A URL representing the system file path of the object to be uploaded.
  165. * - metadata: `StorageMetadata` containing additional information (MIME type, etc.)
  166. * about the object being uploaded.
  167. * - completion: A completion block that either returns the object metadata on success,
  168. * or an error on failure.
  169. * - Returns: An instance of `StorageUploadTask`, which can be used to monitor or manage the upload.
  170. */
  171. @objc(putFile:metadata:completion:) @discardableResult
  172. open func putFile(from fileURL: URL,
  173. metadata: StorageMetadata? = nil,
  174. completion: ((_: StorageMetadata?, _: Error?) -> Void)?) -> StorageUploadTask {
  175. let putMetadata: StorageMetadata = metadata ?? StorageMetadata()
  176. if let path = path.object {
  177. putMetadata.path = path
  178. putMetadata.name = (path as NSString).lastPathComponent as String
  179. }
  180. let task = StorageUploadTask(reference: self,
  181. service: storage.fetcherServiceForApp,
  182. queue: storage.dispatchQueue,
  183. file: fileURL,
  184. metadata: putMetadata)
  185. startAndObserveUploadTask(task: task, completion: completion)
  186. return task
  187. }
  188. // MARK: - Downloads
  189. /**
  190. * Asynchronously downloads the object at the `StorageReference` to a `Data` instance in memory.
  191. * A `Data` buffer of the provided max size will be allocated, so ensure that the device has enough free
  192. * memory to complete the download. For downloading large files, `write(toFile:)` may be a better option.
  193. * - Parameters:
  194. * - maxSize: The maximum size in bytes to download. If the download exceeds this size,
  195. * the task will be cancelled and an error will be returned.
  196. * - completion: A completion block that either returns the object data on success,
  197. * or an error on failure.
  198. * - Returns: An `StorageDownloadTask` that can be used to monitor or manage the download.
  199. */
  200. @objc(dataWithMaxSize:completion:) @discardableResult
  201. open func getData(maxSize: Int64,
  202. completion: @escaping ((_: Data?, _: Error?) -> Void)) -> StorageDownloadTask {
  203. let fetcherService = storage.fetcherServiceForApp
  204. let task = StorageDownloadTask(reference: self,
  205. service: fetcherService,
  206. queue: storage.dispatchQueue,
  207. file: nil)
  208. task.completionData = completion
  209. let callbackQueue = fetcherService.callbackQueue ?? DispatchQueue.main
  210. task.observe(.success) { snapshot in
  211. let error = self.checkSizeOverflow(task: snapshot.task, maxSize: maxSize)
  212. callbackQueue.async {
  213. if let completion = task.completionData {
  214. let data = error == nil ? task.downloadData : nil
  215. completion(data, error)
  216. task.completionData = nil
  217. }
  218. }
  219. }
  220. task.observe(.failure) { snapshot in
  221. callbackQueue.async {
  222. task.completionData?(nil, snapshot.error)
  223. task.completionData = nil
  224. }
  225. }
  226. task.observe(.progress) { snapshot in
  227. if let error = self.checkSizeOverflow(task: snapshot.task, maxSize: maxSize) {
  228. task.cancel(withError: error)
  229. }
  230. }
  231. task.enqueue()
  232. return task
  233. }
  234. /**
  235. * Asynchronously retrieves a long lived download URL with a revokable token.
  236. * This can be used to share the file with others, but can be revoked by a developer
  237. * in the Firebase Console.
  238. * - Parameter completion A completion block that either returns the URL on success,
  239. * or an error on failure.
  240. */
  241. @objc(downloadURLWithCompletion:)
  242. open func downloadURL(completion: @escaping ((_: URL?, _: Error?) -> Void)) {
  243. let fetcherService = storage.fetcherServiceForApp
  244. let task = StorageGetDownloadURLTask(reference: self,
  245. fetcherService: fetcherService,
  246. queue: storage.dispatchQueue,
  247. completion: completion)
  248. task.enqueue()
  249. }
  250. #if compiler(>=5.5) && canImport(_Concurrency)
  251. /**
  252. * Asynchronously retrieves a long lived download URL with a revokable token.
  253. * This can be used to share the file with others, but can be revoked by a developer
  254. * in the Firebase Console.
  255. * - Throws: An error if the download URL could not be retrieved.
  256. * - Returns: The URL on success.
  257. */
  258. @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *)
  259. open func downloadURL() async throws -> URL {
  260. return try await withCheckedThrowingContinuation { continuation in
  261. self.downloadURL { result in
  262. continuation.resume(with: result)
  263. }
  264. }
  265. }
  266. #endif // compiler(>=5.5) && canImport(_Concurrency)
  267. /**
  268. * Asynchronously downloads the object at the current path to a specified system filepath.
  269. * - Parameter fileURL A file system URL representing the path the object should be downloaded to.
  270. * - Returns An `StorageDownloadTask` that can be used to monitor or manage the download.
  271. */
  272. @objc(writeToFile:) @discardableResult
  273. open func write(toFile fileURL: URL) -> StorageDownloadTask {
  274. return write(toFile: fileURL, completion: nil)
  275. }
  276. /**
  277. * Asynchronously downloads the object at the current path to a specified system filepath.
  278. * - Parameters:
  279. * - fileURL: A file system URL representing the path the object should be downloaded to.
  280. * - completion: A closure that fires when the file download completes, passed either
  281. * a URL pointing to the file path of the downloaded file on success,
  282. * or an error on failure.
  283. * - Returns: A `StorageDownloadTask` that can be used to monitor or manage the download.
  284. */
  285. @objc(writeToFile:completion:) @discardableResult
  286. open func write(toFile fileURL: URL,
  287. completion: ((_: URL?, _: Error?) -> Void)?) -> StorageDownloadTask {
  288. let fetcherService = storage.fetcherServiceForApp
  289. let task = StorageDownloadTask(reference: self,
  290. service: fetcherService,
  291. queue: storage.dispatchQueue,
  292. file: fileURL)
  293. if let completion = completion {
  294. task.completionURL = completion
  295. let callbackQueue = fetcherService.callbackQueue ?? DispatchQueue.main
  296. task.observe(.success) { snapshot in
  297. callbackQueue.async {
  298. task.completionURL?(fileURL, nil)
  299. task.completionURL = nil
  300. }
  301. }
  302. task.observe(.failure) { snapshot in
  303. callbackQueue.async {
  304. task.completionURL?(nil, snapshot.error)
  305. task.completionURL = nil
  306. }
  307. }
  308. }
  309. task.enqueue()
  310. return task
  311. }
  312. // MARK: - List Support
  313. /**
  314. * Lists all items (files) and prefixes (folders) under this `StorageReference`.
  315. *
  316. * This is a helper method for calling `list()` repeatedly until there are no more results.
  317. * Consistency of the result is not guaranteed if objects are inserted or removed while this
  318. * operation is executing. All results are buffered in memory.
  319. *
  320. * `listAll(completion:)` is only available for projects using Firebase Rules Version 2.
  321. *
  322. * - Parameter completion A completion handler that will be invoked with all items and prefixes under
  323. * the current `StorageReference`.
  324. */
  325. @objc(listAllWithCompletion:)
  326. open func listAll(completion: @escaping ((_: StorageListResult?, _: Error?) -> Void)) {
  327. let fetcherService = storage.fetcherServiceForApp
  328. var prefixes = [StorageReference]()
  329. var items = [StorageReference]()
  330. weak var weakSelf = self
  331. var paginatedCompletion: ((_: StorageListResult?, _: Error?) -> Void)?
  332. paginatedCompletion = { (_ listResult: StorageListResult?, _ error: Error?) in
  333. if let error = error {
  334. completion(nil, error)
  335. return
  336. }
  337. guard let strongSelf = weakSelf else { return }
  338. guard let listResult = listResult else {
  339. fatalError("internal error: both listResult and error are nil")
  340. }
  341. prefixes.append(contentsOf: listResult.prefixes)
  342. items.append(contentsOf: listResult.items)
  343. if let pageToken = listResult.pageToken {
  344. let nextPage = StorageListTask(reference: strongSelf,
  345. fetcherService: fetcherService,
  346. queue: strongSelf.storage.dispatchQueue,
  347. pageSize: nil,
  348. previousPageToken: pageToken,
  349. completion: paginatedCompletion)
  350. nextPage.enqueue()
  351. } else {
  352. let result = StorageListResult(withPrefixes: prefixes, items: items, pageToken: nil)
  353. // Break the retain cycle we set up indirectly by passing the callback to `nextPage`.
  354. paginatedCompletion = nil
  355. completion(result, nil)
  356. }
  357. }
  358. let task = StorageListTask(reference: self,
  359. fetcherService: fetcherService,
  360. queue: storage.dispatchQueue,
  361. pageSize: nil,
  362. previousPageToken: nil,
  363. completion: paginatedCompletion)
  364. task.enqueue()
  365. }
  366. #if compiler(>=5.5) && canImport(_Concurrency)
  367. /**
  368. * Lists all items (files) and prefixes (folders) under this StorageReference.
  369. *
  370. * This is a helper method for calling list() repeatedly until there are no more results.
  371. * Consistency of the result is not guaranteed if objects are inserted or removed while this
  372. * operation is executing. All results are buffered in memory.
  373. *
  374. * `listAll()` is only available for projects using Firebase Rules Version 2.
  375. *
  376. * - Throws: An error if the list operation failed.
  377. * - Returns: All items and prefixes under the current `StorageReference`.
  378. */
  379. @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *)
  380. open func listAll() async throws -> StorageListResult {
  381. return try await withCheckedThrowingContinuation { continuation in
  382. self.listAll { result in
  383. continuation.resume(with: result)
  384. }
  385. }
  386. }
  387. #endif // compiler(>=5.5) && canImport(_Concurrency)
  388. /**
  389. * List up to `maxResults` items (files) and prefixes (folders) under this StorageReference.
  390. *
  391. * "/" is treated as a path delimiter. Firebase Storage does not support unsupported object
  392. * paths that end with "/" or contain two consecutive "/"s. All invalid objects in GCS will be
  393. * filtered.
  394. *
  395. * `list(maxResults:completion:)` is only available for projects using Firebase Rules Version 2.
  396. *
  397. * - Parameters:
  398. * - maxResults: The maximum number of results to return in a single page. Must be greater
  399. * than 0 and at most 1000.
  400. * - completion: A completion handler that will be invoked with up to `maxResults` items and
  401. * prefixes under the current `StorageReference`.
  402. */
  403. @objc(listWithMaxResults:completion:)
  404. open func list(maxResults: Int64,
  405. completion: @escaping ((_: StorageListResult?, _: Error?) -> Void)) {
  406. if maxResults <= 0 || maxResults > 1000 {
  407. completion(nil,
  408. NSError(domain: StorageErrorDomain,
  409. code: StorageErrorCode.invalidArgument.rawValue,
  410. userInfo: [NSLocalizedDescriptionKey:
  411. "Argument 'maxResults' must be between 1 and 1000 inclusive."]))
  412. } else {
  413. let fetcherService = storage.fetcherServiceForApp
  414. let task = StorageListTask(reference: self,
  415. fetcherService: fetcherService,
  416. queue: storage.dispatchQueue,
  417. pageSize: maxResults,
  418. previousPageToken: nil,
  419. completion: completion)
  420. task.enqueue()
  421. }
  422. }
  423. /**
  424. * Resumes a previous call to `list(maxResults:completion:)`, starting after a pagination token.
  425. * Returns the next set of items (files) and prefixes (folders) under this `StorageReference`.
  426. *
  427. * "/" is treated as a path delimiter. Storage does not support unsupported object
  428. * paths that end with "/" or contain two consecutive "/"s. All invalid objects in GCS will be
  429. * filtered.
  430. *
  431. * `list(maxResults:pageToken:completion:)`is only available for projects using Firebase Rules
  432. * Version 2.
  433. *
  434. * - Parameters:
  435. * - maxResults: The maximum number of results to return in a single page. Must be greater
  436. * than 0 and at most 1000.
  437. * - pageToken: A page token from a previous call to list.
  438. * - completion: A completion handler that will be invoked with the next items and prefixes
  439. * under the current StorageReference.
  440. */
  441. @objc(listWithMaxResults:pageToken:completion:)
  442. open func list(maxResults: Int64,
  443. pageToken: String,
  444. completion: @escaping ((_: StorageListResult?, _: Error?) -> Void)) {
  445. if maxResults <= 0 || maxResults > 1000 {
  446. completion(nil,
  447. NSError(domain: StorageErrorDomain,
  448. code: StorageErrorCode.invalidArgument.rawValue,
  449. userInfo: [NSLocalizedDescriptionKey:
  450. "Argument 'maxResults' must be between 1 and 1000 inclusive."]))
  451. } else {
  452. let fetcherService = storage.fetcherServiceForApp
  453. let task = StorageListTask(reference: self,
  454. fetcherService: fetcherService,
  455. queue: storage.dispatchQueue,
  456. pageSize: maxResults,
  457. previousPageToken: pageToken,
  458. completion: completion)
  459. task.enqueue()
  460. }
  461. }
  462. // MARK: - Metadata Operations
  463. /**
  464. * Retrieves metadata associated with an object at the current path.
  465. * - Parameter completion A completion block which returns the object metadata on success,
  466. * or an error on failure.
  467. */
  468. @objc(metadataWithCompletion:)
  469. open func getMetadata(completion: @escaping ((_: StorageMetadata?, _: Error?) -> Void)) {
  470. let fetcherService = storage.fetcherServiceForApp
  471. let task = StorageGetMetadataTask(reference: self,
  472. fetcherService: fetcherService,
  473. queue: storage.dispatchQueue,
  474. completion: completion)
  475. task.enqueue()
  476. }
  477. #if compiler(>=5.5) && canImport(_Concurrency)
  478. /**
  479. * Retrieves metadata associated with an object at the current path.
  480. * - Throws: An error if the object metadata could not be retrieved.
  481. * - Returns: The object metadata on success.
  482. */
  483. @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *)
  484. open func getMetadata() async throws -> StorageMetadata {
  485. return try await withCheckedThrowingContinuation { continuation in
  486. self.getMetadata { result in
  487. continuation.resume(with: result)
  488. }
  489. }
  490. }
  491. #endif // compiler(>=5.5) && canImport(_Concurrency)
  492. /**
  493. * Updates the metadata associated with an object at the current path.
  494. * - Parameters:
  495. * - metadata: A `StorageMetadata` object with the metadata to update.
  496. * - completion: A completion block which returns the `StorageMetadata` on success,
  497. * or an error on failure.
  498. */
  499. @objc(updateMetadata:completion:)
  500. open func updateMetadata(_ metadata: StorageMetadata,
  501. completion: ((_: StorageMetadata?, _: Error?) -> Void)?) {
  502. let fetcherService = storage.fetcherServiceForApp
  503. let task = StorageUpdateMetadataTask(reference: self,
  504. fetcherService: fetcherService,
  505. queue: storage.dispatchQueue,
  506. metadata: metadata,
  507. completion: completion)
  508. task.enqueue()
  509. }
  510. #if compiler(>=5.5) && canImport(_Concurrency)
  511. /**
  512. * Updates the metadata associated with an object at the current path.
  513. * - Parameter metadata A `StorageMetadata` object with the metadata to update.
  514. * - Throws: An error if the metadata update operation failed.
  515. * - Returns: The object metadata on success.
  516. */
  517. @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *)
  518. open func updateMetadata(_ metadata: StorageMetadata) async throws -> StorageMetadata {
  519. return try await withCheckedThrowingContinuation { continuation in
  520. self.updateMetadata(metadata) { result in
  521. continuation.resume(with: result)
  522. }
  523. }
  524. }
  525. #endif // compiler(>=5.5) && canImport(_Concurrency)
  526. // MARK: - Delete
  527. /**
  528. * Deletes the object at the current path.
  529. * - Parameter completion A completion block which returns a nonnull error on failure.
  530. */
  531. @objc(deleteWithCompletion:)
  532. open func delete(completion: ((_: Error?) -> Void)?) {
  533. let fetcherService = storage.fetcherServiceForApp
  534. let task = StorageDeleteTask(reference: self,
  535. fetcherService: fetcherService,
  536. queue: storage.dispatchQueue,
  537. completion: completion)
  538. task.enqueue()
  539. }
  540. #if compiler(>=5.5) && canImport(_Concurrency)
  541. /**
  542. * Deletes the object at the current path.
  543. * - Throws: An error if the delete operation failed.
  544. */
  545. @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *)
  546. open func delete() async throws {
  547. return try await withCheckedThrowingContinuation { continuation in
  548. self.delete { error in
  549. if let error = error {
  550. continuation.resume(throwing: error)
  551. } else {
  552. continuation.resume()
  553. }
  554. }
  555. }
  556. }
  557. #endif // compiler(>=5.5) && canImport(_Concurrency)
  558. // MARK: - NSObject overrides
  559. @objc override open func copy() -> Any {
  560. return StorageReference(storage: storage, path: path)
  561. }
  562. @objc override open func isEqual(_ object: Any?) -> Bool {
  563. guard let ref = object as? StorageReference else {
  564. return false
  565. }
  566. return storage == ref.storage && path == ref.path
  567. }
  568. @objc override public var hash: Int {
  569. return storage.hash ^ path.bucket.hashValue
  570. }
  571. @objc override public var description: String {
  572. return "gs://\(path.bucket)/\(path.object ?? "")"
  573. }
  574. // MARK: - Internal APIs
  575. /**
  576. * The current path which points to an object in the Google Cloud Storage bucket.
  577. */
  578. let path: StoragePath
  579. override init() {
  580. storage = Storage.storage()
  581. let storageBucket = storage.app.options.storageBucket!
  582. path = StoragePath(with: storageBucket)
  583. }
  584. init(storage: Storage, path: StoragePath) {
  585. self.storage = storage
  586. self.path = path
  587. }
  588. /**
  589. * For maxSize API, return an error if the size is exceeded.
  590. */
  591. private func checkSizeOverflow(task: StorageTask, maxSize: Int64) -> NSError? {
  592. if task.progress.totalUnitCount > maxSize || task.progress.completedUnitCount > maxSize {
  593. return StorageErrorCode.error(withCode: .downloadSizeExceeded,
  594. infoDictionary: [
  595. "totalSize": task.progress.totalUnitCount,
  596. "maxAllowedSize": maxSize,
  597. ])
  598. }
  599. return nil
  600. }
  601. private func startAndObserveUploadTask(task: StorageUploadTask,
  602. completion: ((_: StorageMetadata?, _: Error?) -> Void)?) {
  603. if let completion = completion {
  604. task.completionMetadata = completion
  605. let callbackQueue = storage.fetcherServiceForApp.callbackQueue ?? DispatchQueue.main
  606. task.observe(.success) { snapshot in
  607. callbackQueue.async {
  608. task.completionMetadata?(snapshot.metadata, nil)
  609. task.completionMetadata = nil
  610. }
  611. }
  612. task.observe(.failure) { snapshot in
  613. callbackQueue.async {
  614. task.completionMetadata?(nil, snapshot.error)
  615. task.completionMetadata = nil
  616. }
  617. }
  618. }
  619. task.enqueue()
  620. }
  621. }