StorageReference.swift 28 KB

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