StorageReference.swift 28 KB

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