StorageReference.swift 27 KB

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