StorageReference.swift 27 KB

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