Pārlūkot izejas kodu

[v11] Streamline internal FirebaseStorageTask implementations (#13220)

Paul Beusterien 1 gadu atpakaļ
vecāks
revīzija
a53db1a9ba
26 mainītis faili ar 316 papildinājumiem un 502 dzēšanām
  1. 13 54
      FirebaseStorage/Sources/Internal/StorageDeleteTask.swift
  2. 30 67
      FirebaseStorage/Sources/Internal/StorageGetDownloadURLTask.swift
  3. 22 63
      FirebaseStorage/Sources/Internal/StorageGetMetadataTask.swift
  4. 64 0
      FirebaseStorage/Sources/Internal/StorageInternalTask.swift
  5. 49 107
      FirebaseStorage/Sources/Internal/StorageListTask.swift
  6. 1 0
      FirebaseStorage/Sources/Internal/StorageTokenAuthorizer.swift
  7. 30 71
      FirebaseStorage/Sources/Internal/StorageUpdateMetadataTask.swift
  8. 2 11
      FirebaseStorage/Sources/Internal/StorageUtils.swift
  9. 2 0
      FirebaseStorage/Sources/Result.swift
  10. 4 2
      FirebaseStorage/Sources/Storage.swift
  11. 1 0
      FirebaseStorage/Sources/StorageDownloadTask.swift
  12. 2 0
      FirebaseStorage/Sources/StorageError.swift
  13. 1 0
      FirebaseStorage/Sources/StorageListResult.swift
  14. 1 0
      FirebaseStorage/Sources/StorageMetadata.swift
  15. 1 0
      FirebaseStorage/Sources/StorageObservableTask.swift
  16. 45 47
      FirebaseStorage/Sources/StorageReference.swift
  17. 1 0
      FirebaseStorage/Sources/StorageTask.swift
  18. 1 0
      FirebaseStorage/Sources/StorageTaskSnapshot.swift
  19. 1 0
      FirebaseStorage/Sources/StorageUploadTask.swift
  20. 1 0
      FirebaseStorage/Tests/Integration/StorageIntegration.swift
  21. 13 18
      FirebaseStorage/Tests/Unit/StorageDeleteTests.swift
  22. 8 14
      FirebaseStorage/Tests/Unit/StorageGetMetadataTests.swift
  23. 9 18
      FirebaseStorage/Tests/Unit/StorageListTests.swift
  24. 6 15
      FirebaseStorage/Tests/Unit/StorageMetadataTests.swift
  25. 1 1
      FirebaseStorage/Tests/Unit/StorageReferenceTests.swift
  26. 7 14
      FirebaseStorage/Tests/Unit/StorageUpdateMetadataTests.swift

+ 13 - 54
FirebaseStorage/Sources/Internal/StorageDeleteTask.swift

@@ -20,59 +20,18 @@ import Foundation
   import GTMSessionFetcherCore
 #endif
 
-/**
- * Task which provides the ability to delete an object in Firebase Storage.
- */
-class StorageDeleteTask: StorageTask, StorageTaskManagement {
-  private var fetcher: GTMSessionFetcher?
-  private var fetcherCompletion: ((Data?, NSError?) -> Void)?
-  private var taskCompletion: ((_ error: Error?) -> Void)?
-
-  init(reference: StorageReference,
-       fetcherService: GTMSessionFetcherService,
-       queue: DispatchQueue,
-       completion: ((_: Error?) -> Void)?) {
-    super.init(reference: reference, service: fetcherService, queue: queue)
-    taskCompletion = completion
-  }
-
-  deinit {
-    self.fetcher?.stopFetching()
-  }
-
-  /**
-   * Prepares a task and begins execution.
-   */
-  func enqueue() {
-    let completion = taskCompletion
-    taskCompletion = { (error: Error?) in
-      completion?(error)
-      // Reference self in completion handler in order to retain self until completion is called.
-      self.taskCompletion = nil
-    }
-    dispatchQueue.async { [weak self] in
-      guard let self = self else { return }
-      self.state = .queueing
-      var request = self.baseRequest
-      request.httpMethod = "DELETE"
-      request.timeoutInterval = self.reference.storage.maxOperationRetryTime
-
-      let fetcher = self.fetcherService.fetcher(with: request)
-      fetcher.comment = "DeleteTask"
-      self.fetcher = fetcher
-
-      self.fetcherCompletion = { [weak self] (data: Data?, error: NSError?) in
-        guard let self = self else { return }
-        if let error, self.error == nil {
-          self.error = StorageErrorCode.error(withServerError: error, ref: self.reference)
-        }
-        self.taskCompletion?(self.error)
-        self.fetcherCompletion = nil
-      }
-
-      self.fetcher?.beginFetch { [weak self] data, error in
-        self?.fetcherCompletion?(data, error as? NSError)
-      }
-    }
+/// Task which provides the ability to delete an object in Firebase Storage.
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+enum StorageDeleteTask {
+  static func deleteTask(reference: StorageReference,
+                         fetcherService: GTMSessionFetcherService,
+                         queue: DispatchQueue,
+                         completion: ((_: Data?, _: Error?) -> Void)?) {
+    StorageInternalTask(reference: reference,
+                        fetcherService: fetcherService,
+                        queue: queue,
+                        httpMethod: "DELETE",
+                        fetcherComment: "DeleteTask",
+                        completion: completion)
   }
 }

+ 30 - 67
FirebaseStorage/Sources/Internal/StorageGetDownloadURLTask.swift

@@ -20,80 +20,43 @@ import Foundation
   import GTMSessionFetcherCore
 #endif
 
-/**
- * Task which provides the ability to get a download URL for an object in Firebase Storage.
- */
-class StorageGetDownloadURLTask: StorageTask, StorageTaskManagement {
-  private var fetcher: GTMSessionFetcher?
-  private var fetcherCompletion: ((Data?, NSError?) -> Void)?
-  private var taskCompletion: ((_ downloadURL: URL?, _: Error?) -> Void)?
-
-  init(reference: StorageReference,
-       fetcherService: GTMSessionFetcherService,
-       queue: DispatchQueue,
-       completion: ((_: URL?, _: Error?) -> Void)?) {
-    super.init(reference: reference, service: fetcherService, queue: queue)
-    taskCompletion = completion
-  }
-
-  deinit {
-    self.fetcher?.stopFetching()
-  }
-
-  /**
-   * Prepares a task and begins execution.
-   */
-  func enqueue() {
-    if let completion = taskCompletion {
-      taskCompletion = { (url: URL?, error: Error?) in
-        completion(url, error)
-        // Reference self in completion handler in order to retain self until completion is called.
-        self.taskCompletion = nil
-      }
-    }
-    dispatchQueue.async { [weak self] in
-      guard let self = self else { return }
-      var request = self.baseRequest
-      request.httpMethod = "GET"
-      request.timeoutInterval = self.reference.storage.maxOperationRetryTime
-
-      let fetcher = self.fetcherService.fetcher(with: request)
-      fetcher.comment = "GetDownloadURLTask"
-      self.fetcher = fetcher
-
-      self.fetcherCompletion = { [weak self] (data: Data?, error: NSError?) in
-        guard let self = self else { return }
-        var downloadURL: URL?
-        if let error {
-          if self.error == nil {
-            self.error = StorageErrorCode.error(withServerError: error, ref: self.reference)
+/// Task which provides the ability to get a download URL for an object in Firebase Storage.
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+enum StorageGetDownloadURLTask {
+  static func getDownloadURLTask(reference: StorageReference,
+                                 fetcherService: GTMSessionFetcherService,
+                                 queue: DispatchQueue,
+                                 completion: ((_: URL?, _: Error?) -> Void)?) {
+    StorageInternalTask(reference: reference,
+                        fetcherService: fetcherService,
+                        queue: queue,
+                        httpMethod: "GET",
+                        fetcherComment: "GetDownloadURLTask") { (data: Data?, error: Error?) in
+      if let error {
+        completion?(nil, error)
+      } else {
+        if let data,
+           let responseDictionary = try? JSONSerialization
+           .jsonObject(with: data) as? [String: Any] {
+          guard let downloadURL = downloadURLFromMetadataDictionary(responseDictionary,
+                                                                    reference) else {
+            let error = StorageError.unknown(
+              message: "Failed to retrieve a download URL.",
+              serverError: [:]
+            ) as NSError
+            completion?(nil, error)
+            return
           }
+          completion?(downloadURL, nil)
         } else {
-          if let data,
-             let responseDictionary = try? JSONSerialization
-             .jsonObject(with: data) as? [String: Any] {
-            downloadURL = self.downloadURLFromMetadataDictionary(responseDictionary)
-            if downloadURL == nil {
-              self.error = StorageError.unknown(
-                message: "Failed to retrieve a download URL.",
-                serverError: [:]
-              ) as NSError
-            }
-          } else {
-            self.error = StorageErrorCode.error(withInvalidRequest: data)
-          }
+          completion?(nil, StorageErrorCode.error(withInvalidRequest: data))
         }
-        self.taskCompletion?(downloadURL, self.error)
-        self.fetcherCompletion = nil
-      }
-
-      self.fetcher?.beginFetch { [weak self] data, error in
-        self?.fetcherCompletion?(data, error as? NSError)
       }
     }
   }
 
-  func downloadURLFromMetadataDictionary(_ dictionary: [String: Any]) -> URL? {
+  static func downloadURLFromMetadataDictionary(_ dictionary: [String: Any],
+                                                _ reference: StorageReference) -> URL? {
     let downloadTokens = dictionary["downloadTokens"]
     guard let downloadTokens = downloadTokens as? String,
           downloadTokens.count > 0 else {

+ 22 - 63
FirebaseStorage/Sources/Internal/StorageGetMetadataTask.swift

@@ -20,71 +20,30 @@ import Foundation
   import GTMSessionFetcherCore
 #endif
 
-/**
- * Task which provides the ability to delete an object in Firebase Storage.
- */
-class StorageGetMetadataTask: StorageTask, StorageTaskManagement {
-  private var fetcher: GTMSessionFetcher?
-  private var fetcherCompletion: ((Data?, NSError?) -> Void)?
-  private var taskCompletion: ((_ metadata: StorageMetadata?, _: Error?) -> Void)?
-
-  init(reference: StorageReference,
-       fetcherService: GTMSessionFetcherService,
-       queue: DispatchQueue,
-       completion: ((_: StorageMetadata?, _: Error?) -> Void)?) {
-    super.init(reference: reference, service: fetcherService, queue: queue)
-    taskCompletion = completion
-  }
-
-  deinit {
-    self.fetcher?.stopFetching()
-  }
-
-  /**
-   * Prepares a task and begins execution.
-   */
-  func enqueue() {
-    if let completion = taskCompletion {
-      taskCompletion = { (metadata: StorageMetadata?, error: Error?) in
-        completion(metadata, error)
-        // Reference self in completion handler in order to retain self until completion is called.
-        self.taskCompletion = nil
-      }
-    }
-    dispatchQueue.async { [weak self] in
-      guard let self = self else { return }
-      self.state = .queueing
-      var request = self.baseRequest
-      request.httpMethod = "GET"
-      request.timeoutInterval = self.reference.storage.maxOperationRetryTime
-
-      let fetcher = self.fetcherService.fetcher(with: request)
-      fetcher.comment = "GetMetadataTask"
-      self.fetcher = fetcher
-
-      self.fetcherCompletion = { [weak self] (data: Data?, error: NSError?) in
-        guard let self = self else { return }
-        var metadata: StorageMetadata?
-        if let error {
-          if self.error == nil {
-            self.error = StorageErrorCode.error(withServerError: error, ref: self.reference)
-          }
+/// Task which provides the ability to delete an object in Firebase Storage.
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+enum StorageGetMetadataTask {
+  static func getMetadataTask(reference: StorageReference,
+                              fetcherService: GTMSessionFetcherService,
+                              queue: DispatchQueue,
+                              completion: ((_: StorageMetadata?, _: Error?) -> Void)?) {
+    StorageInternalTask(reference: reference,
+                        fetcherService: fetcherService,
+                        queue: queue,
+                        httpMethod: "GET",
+                        fetcherComment: "GetMetadataTask") { (data: Data?, error: Error?) in
+      if let error {
+        completion?(nil, error)
+      } else {
+        if let data,
+           let responseDictionary = try? JSONSerialization
+           .jsonObject(with: data) as? [String: AnyHashable] {
+          let metadata = StorageMetadata(dictionary: responseDictionary)
+          metadata.fileType = .file
+          completion?(metadata, nil)
         } else {
-          if let data,
-             let responseDictionary = try? JSONSerialization
-             .jsonObject(with: data) as? [String: AnyHashable] {
-            metadata = StorageMetadata(dictionary: responseDictionary)
-            metadata?.fileType = .file
-          } else {
-            self.error = StorageErrorCode.error(withInvalidRequest: data)
-          }
+          completion?(nil, StorageErrorCode.error(withInvalidRequest: data))
         }
-        self.taskCompletion?(metadata, self.error)
-        self.fetcherCompletion = nil
-      }
-
-      self.fetcher?.beginFetch { [weak self] data, error in
-        self?.fetcherCompletion?(data, error as? NSError)
       }
     }
   }

+ 64 - 0
FirebaseStorage/Sources/Internal/StorageInternalTask.swift

@@ -0,0 +1,64 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import Foundation
+
+#if COCOAPODS
+  import GTMSessionFetcher
+#else
+  import GTMSessionFetcherCore
+#endif
+
+/// Implement StorageTasks that are not directly exposed via the public API.
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+class StorageInternalTask: StorageTask {
+  private var fetcher: GTMSessionFetcher?
+
+  @discardableResult
+  init(reference: StorageReference,
+       fetcherService: GTMSessionFetcherService,
+       queue: DispatchQueue,
+       request: URLRequest? = nil,
+       httpMethod: String,
+       fetcherComment: String,
+       completion: ((_: Data?, _: Error?) -> Void)?) {
+    super.init(reference: reference, service: fetcherService, queue: queue)
+
+    // Prepare a task and begins execution.
+    dispatchQueue.async { [self] in
+      self.state = .queueing
+      var request = request ?? self.baseRequest
+      request.httpMethod = httpMethod
+      request.timeoutInterval = self.reference.storage.maxOperationRetryTime
+
+      let fetcher = self.fetcherService.fetcher(with: request)
+      fetcher.comment = fetcherComment
+      self.fetcher = fetcher
+
+      Task {
+        do {
+          let data = try await self.fetcher?.beginFetch()
+          completion?(data, nil)
+        } catch {
+          completion?(nil, StorageErrorCode.error(withServerError: error as NSError,
+                                                  ref: self.reference))
+        }
+      }
+    }
+  }
+
+  deinit {
+    self.fetcher?.stopFetching()
+  }
+}

+ 49 - 107
FirebaseStorage/Sources/Internal/StorageListTask.swift

@@ -20,120 +20,62 @@ import Foundation
   import GTMSessionFetcherCore
 #endif
 
-/**
- * A Task that lists the entries under a {@link StorageReference}
- */
-class StorageListTask: StorageTask, StorageTaskManagement {
-  private var fetcher: GTMSessionFetcher?
-  private var fetcherCompletion: ((Data?, NSError?) -> Void)?
-  private var taskCompletion: ((_: StorageListResult?, _: NSError?) -> Void)?
-  private let pageSize: Int64?
-  private let previousPageToken: String?
-
-  /**
-   * Initializes a new List Task.
-   *
-   * To schedule the task, invoke `[FIRStorageListTask enqueue]`.
-   *
-   * @param reference The location to invoke List on.
-   * @param service GTMSessionFetcherService to use for the RPC.
-   * @param queue The queue to schedule the List operation on.
-   * @param pageSize An optional pageSize, denoting the maximum size of the result set. If
-   * set to `nil`, the backend will use the default page size.
-   * @param previousPageToken An optional pageToken, used to resume a previous invocation.
-   * @param completion The completion handler to be called with the FIRIMPLStorageListResult.
-   */
-  init(reference: StorageReference,
-       fetcherService: GTMSessionFetcherService,
-       queue: DispatchQueue,
-       pageSize: Int64?,
-       previousPageToken: String?,
-       completion: ((_ listResult: StorageListResult?, _ error: NSError?) -> Void)?) {
-    self.pageSize = pageSize
-    self.previousPageToken = previousPageToken
-    super.init(reference: reference, service: fetcherService, queue: queue)
-    taskCompletion = { (listResult: StorageListResult?, error: NSError?) in
-      completion?(listResult, error)
-      // Reference self in completion handler in order to retain self until completion is called.
-      self.taskCompletion = nil
+/// A Task that lists the entries under a StorageReference
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+enum StorageListTask {
+  static func listTask(reference: StorageReference,
+                       fetcherService: GTMSessionFetcherService,
+                       queue: DispatchQueue,
+                       pageSize: Int64?,
+                       previousPageToken: String?,
+                       completion: ((_: StorageListResult?, _: Error?) -> Void)?) {
+    var queryParams = [String: String]()
+
+    let prefix = reference.fullPath
+    if prefix.count > 0 {
+      queryParams["prefix"] = "\(prefix)/"
     }
-  }
 
-  deinit {
-    self.fetcher?.stopFetching()
-  }
-
-  /**
-   * Prepares a task and begins execution.
-   */
-  func enqueue() {
-    if let completion = taskCompletion {
-      taskCompletion = { (listResult: StorageListResult?, error: NSError?) in
-        completion(listResult, error)
-        // Reference self in completion handler in order to retain self until completion is called.
-        self.taskCompletion = nil
-      }
+    // Firebase Storage uses file system semantics and treats slashes as separators. GCS's List
+    // API
+    // does not prescribe a separator, and hence we need to provide a slash as the delimiter.
+    queryParams["delimiter"] = "/"
+
+    // listAll() doesn't set a pageSize as this allows Firebase Storage to determine how many
+    // items
+    // to return per page. This removes the need to backfill results if Firebase Storage filters
+    // objects that are considered invalid (such as items with two consecutive slashes).
+    if let pageSize {
+      queryParams["maxResults"] = "\(pageSize)"
     }
-    dispatchQueue.async { [weak self] in
-      guard let self = self else { return }
-      var queryParams = [String: String]()
-
-      let prefix = self.reference.fullPath
-      if prefix.count > 0 {
-        queryParams["prefix"] = "\(prefix)/"
-      }
-
-      // Firebase Storage uses file system semantics and treats slashes as separators. GCS's List
-      // API
-      // does not prescribe a separator, and hence we need to provide a slash as the delimiter.
-      queryParams["delimiter"] = "/"
-
-      // listAll() doesn't set a pageSize as this allows Firebase Storage to determine how many
-      // items
-      // to return per page. This removes the need to backfill results if Firebase Storage filters
-      // objects that are considered invalid (such as items with two consecutive slashes).
-      if let pageSize {
-        queryParams["maxResults"] = "\(pageSize)"
-      }
-
-      if let previousPageToken {
-        queryParams["pageToken"] = previousPageToken
-      }
-
-      let root = self.reference.root()
-      var request = StorageUtils.defaultRequestForReference(
-        reference: root,
-        queryParams: queryParams
-      )
-
-      request.httpMethod = "GET"
-      request.timeoutInterval = self.reference.storage.maxOperationRetryTime
 
-      let fetcher = self.fetcherService.fetcher(with: request)
-      fetcher.comment = "ListTask"
-      self.fetcher = fetcher
+    if let previousPageToken {
+      queryParams["pageToken"] = previousPageToken
+    }
 
-      self.fetcherCompletion = { [weak self] (data: Data?, error: NSError?) in
-        guard let self = self else { return }
-        var listResult: StorageListResult?
-        if let error, self.error == nil {
-          self.error = StorageErrorCode.error(withServerError: error, ref: self.reference)
+    let root = reference.root()
+    let request = StorageUtils.defaultRequestForReference(
+      reference: root,
+      queryParams: queryParams
+    )
+
+    StorageInternalTask(reference: reference,
+                        fetcherService: fetcherService,
+                        queue: queue,
+                        request: request,
+                        httpMethod: "GET",
+                        fetcherComment: "ListTask") { (data: Data?, error: Error?) in
+      if let error {
+        completion?(nil, error)
+      } else {
+        if let data,
+           let responseDictionary = try? JSONSerialization
+           .jsonObject(with: data) as? [String: AnyHashable] {
+          let listResult = StorageListResult(with: responseDictionary, reference: reference)
+          completion?(listResult, nil)
         } else {
-          if let data,
-             let responseDictionary = try? JSONSerialization
-             .jsonObject(with: data) as? [String: Any] {
-            listResult = StorageListResult(with: responseDictionary, reference: self.reference)
-          } else {
-            self.error = StorageErrorCode.error(withInvalidRequest: data)
-          }
+          completion?(nil, StorageErrorCode.error(withInvalidRequest: data))
         }
-
-        self.taskCompletion?(listResult, self.error)
-        self.fetcherCompletion = nil
-      }
-
-      self.fetcher?.beginFetch { [weak self] data, error in
-        self?.fetcherCompletion?(data, error as? NSError)
       }
     }
   }

+ 1 - 0
FirebaseStorage/Sources/Internal/StorageTokenAuthorizer.swift

@@ -25,6 +25,7 @@ import FirebaseCore
   import GTMSessionFetcherCore
 #endif
 
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
 class StorageTokenAuthorizer: NSObject, GTMSessionFetcherAuthorizer {
   func authorizeRequest(_ request: NSMutableURLRequest?,
                         completionHandler handler: @escaping (Error?) -> Void) {

+ 30 - 71
FirebaseStorage/Sources/Internal/StorageUpdateMetadataTask.swift

@@ -20,81 +20,40 @@ import Foundation
   import GTMSessionFetcherCore
 #endif
 
-/**
- * Task which provides the ability to delete an object in Firebase Storage.
- */
-class StorageUpdateMetadataTask: StorageTask, StorageTaskManagement {
-  private var fetcher: GTMSessionFetcher?
-  private var fetcherCompletion: ((Data?, NSError?) -> Void)?
-  private var taskCompletion: ((_ metadata: StorageMetadata?, _: Error?) -> Void)?
-  private var updateMetadata: StorageMetadata
-
-  init(reference: StorageReference,
-       fetcherService: GTMSessionFetcherService,
-       queue: DispatchQueue,
-       metadata: StorageMetadata,
-       completion: ((_: StorageMetadata?, _: Error?) -> Void)?) {
-    updateMetadata = metadata
-    super.init(reference: reference, service: fetcherService, queue: queue)
-    taskCompletion = completion
-  }
-
-  deinit {
-    self.fetcher?.stopFetching()
-  }
-
-  /**
-   * Prepares a task and begins execution.
-   */
-  func enqueue() {
-    let completion = taskCompletion
-    taskCompletion = { (metadata: StorageMetadata?, error: Error?) in
-      completion?(metadata, error)
-      // Reference self in completion handler in order to retain self until completion is called.
-      self.taskCompletion = nil
+/// A Task that lists the entries under a StorageReference
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+enum StorageUpdateMetadataTask {
+  static func updateMetadataTask(reference: StorageReference,
+                                 fetcherService: GTMSessionFetcherService,
+                                 queue: DispatchQueue,
+                                 metadata: StorageMetadata,
+                                 completion: ((_: StorageMetadata?, _: Error?) -> Void)?) {
+    var request = StorageUtils.defaultRequestForReference(reference: reference)
+    let updateData = try? JSONSerialization.data(withJSONObject: metadata.updatedMetadata())
+    request.httpBody = updateData
+    request.setValue("application/json; charset=UTF-8", forHTTPHeaderField: "Content-Type")
+    if let count = updateData?.count {
+      request.setValue("\(count)", forHTTPHeaderField: "Content-Length")
     }
-    dispatchQueue.async { [weak self] in
-      guard let self = self else { return }
-      var request = self.baseRequest
-      let updateDictionary = self.updateMetadata.updatedMetadata()
-      let updateData = try? JSONSerialization.data(withJSONObject: updateDictionary)
-      request.httpMethod = "PATCH"
-      request.timeoutInterval = self.reference.storage.maxOperationRetryTime
-      request.httpBody = updateData
-      request.setValue("application/json; charset=UTF-8", forHTTPHeaderField: "Content-Type")
-      if let count = updateData?.count {
-        request.setValue("\(count)", forHTTPHeaderField: "Content-Length")
-      }
-
-      let fetcher = self.fetcherService.fetcher(with: request)
-      fetcher.comment = "GetMetadataTask"
-      self.fetcher = fetcher
 
-      self.fetcherCompletion = { [weak self] (data: Data?, error: NSError?) in
-        guard let self = self else { return }
-        var metadata: StorageMetadata?
-        if let error {
-          if self.error == nil {
-            self.error = StorageErrorCode.error(withServerError: error, ref: self.reference)
-          }
+    StorageInternalTask(reference: reference,
+                        fetcherService: fetcherService,
+                        queue: queue,
+                        request: request,
+                        httpMethod: "PATCH",
+                        fetcherComment: "GetMetadataTask") { (data: Data?, error: Error?) in
+      if let error {
+        completion?(nil, error)
+      } else {
+        if let data,
+           let responseDictionary = try? JSONSerialization
+           .jsonObject(with: data) as? [String: AnyHashable] {
+          let metadata = StorageMetadata(dictionary: responseDictionary)
+          metadata.fileType = .file
+          completion?(metadata, nil)
         } else {
-          if let data,
-             let responseDictionary = try? JSONSerialization
-             .jsonObject(with: data) as? [String: AnyHashable] {
-            metadata = StorageMetadata(dictionary: responseDictionary)
-            metadata?.fileType = .file
-          } else {
-            self.error = StorageErrorCode.error(withInvalidRequest: data)
-          }
+          completion?(nil, StorageErrorCode.error(withInvalidRequest: data))
         }
-        self.taskCompletion?(metadata, self.error)
-        self.fetcherCompletion = nil
-      }
-
-      fetcher.comment = "UpdateMetadataTask"
-
-      self.fetcher?.beginFetch { [weak self] data, error in
-        self?.fetcherCompletion?(data, error as? NSError)
       }
     }
   }

+ 2 - 11
FirebaseStorage/Sources/Internal/StorageUtils.swift

@@ -13,22 +13,13 @@
 // limitations under the License.
 
 import Foundation
-#if os(iOS) || os(tvOS)
+#if os(iOS) || os(tvOS) || os(visionOS)
   import MobileCoreServices
 #elseif os(macOS) || os(watchOS)
   import CoreServices
 #endif // os(iOS) || os(tvOS)
 
-// swift(>=5.9) implies Xcode 15+
-// Need to have this Swift version check to use os(visionOS) macro, VisionOS support.
-// TODO: Remove this check and add `os(visionOS)` to the `os(iOS) || os(tvOS)` conditional above
-// when Xcode 15 is the minimum supported by Firebase.
-#if swift(>=5.9)
-  #if os(visionOS)
-    import MobileCoreServices
-  #endif // os(visionOS)
-#endif // swift(>=5.9)
-
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
 class StorageUtils {
   class func defaultRequestForReference(reference: StorageReference,
                                         queryParams: [String: String]? = nil)

+ 2 - 0
FirebaseStorage/Sources/Result.swift

@@ -22,6 +22,7 @@ import Foundation
 ///                 an `Error`.
 /// - Returns: A closure parameterized with an optional generic and optional `Error` to match
 ///            Objective-C APIs.
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
 private func getResultCallback<T>(completion: @escaping (Result<T, Error>) -> Void) -> (_: T?,
                                                                                         _: Error?)
   -> Void {
@@ -38,6 +39,7 @@ private func getResultCallback<T>(completion: @escaping (Result<T, Error>) -> Vo
   }
 }
 
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
 public extension StorageReference {
   /// Asynchronously retrieves a long lived download URL with a revokable token.
   ///

+ 4 - 2
FirebaseStorage/Sources/Storage.swift

@@ -36,6 +36,7 @@ import FirebaseCore
 ///
 /// If you provide a custom instance of `FirebaseApp`,
 /// the storage location will be specified via the `FirebaseOptions.storageBucket` property.
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
 @objc(FIRStorage) open class Storage: NSObject {
   // MARK: - Public APIs
 
@@ -138,6 +139,7 @@ import FirebaseCore
 
   /// Creates a `StorageReference` initialized at the root Firebase Storage location.
   /// - Returns: An instance of `StorageReference` referencing the root of the storage bucket.
+  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
   @objc open func reference() -> StorageReference {
     ensureConfigured()
     let path = StoragePath(with: storageBucket)
@@ -153,8 +155,8 @@ import FirebaseCore
   /// - Parameter url: A gs:// or https:// URL to initialize the reference with.
   /// - Returns: An instance of StorageReference at the given child path.
   /// - Throws: Throws a fatal error if `url` is not associated with the `FirebaseApp` used to
-  /// initialize
-  ///     this Storage instance.
+  /// initialize this Storage instance.
+  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
   @objc open func reference(forURL url: String) -> StorageReference {
     ensureConfigured()
     do {

+ 1 - 0
FirebaseStorage/Sources/StorageDownloadTask.swift

@@ -33,6 +33,7 @@ import Foundation
  * specified `callbackQueue` in Storage, or the main queue if left unspecified.
  */
 @objc(FIRStorageDownloadTask)
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
 open class StorageDownloadTask: StorageObservableTask, StorageTaskManagement {
   /**
    * Prepares a task and begins execution.

+ 2 - 0
FirebaseStorage/Sources/StorageError.swift

@@ -23,6 +23,7 @@ public let StorageErrorDomain: String = "FIRStorageErrorDomain"
  * https://cloud.google.com/storage/docs/json_api/v1/status-codes
  * This is never publicly exposed to end developers (as they will simply see an NSError).
  */
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
 @objc(FIRStorageErrorCode) public enum StorageErrorCode: Int, Swift.Error {
   case unknown = -13000
   case objectNotFound = -13010
@@ -102,6 +103,7 @@ public let StorageErrorDomain: String = "FIRStorageErrorDomain"
 }
 
 /// Firebase Storage errors
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
 public enum StorageError: Error, CustomNSError {
   case unknown(message: String, serverError: [String: Any])
   case objectNotFound(object: String, serverError: [String: Any])

+ 1 - 0
FirebaseStorage/Sources/StorageListResult.swift

@@ -15,6 +15,7 @@
 import Foundation
 
 /** Contains the prefixes and items returned by a `StorageReference.list()` call. */
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
 @objc(FIRStorageListResult) open class StorageListResult: NSObject {
   /**
    * The prefixes (folders) returned by a `list()` operation.

+ 1 - 0
FirebaseStorage/Sources/StorageMetadata.swift

@@ -22,6 +22,7 @@ import Foundation
  * and a Storage reference to the object in question. Full documentation can be found in the
  * [GCS documentation](https://cloud.google.com/storage/docs/json_api/v1/objects#resource)
  */
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
 @objc(FIRStorageMetadata) open class StorageMetadata: NSObject {
   // MARK: - Public APIs
 

+ 1 - 0
FirebaseStorage/Sources/StorageObservableTask.swift

@@ -27,6 +27,7 @@ import Foundation
  * Observers produce a `StorageHandle`, which is used to keep track of and remove specific
  * observers at a later date.
  */
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
 @objc(FIRStorageObservableTask) open class StorageObservableTask: StorageTask {
   /**
    * Observes changes in the upload status: Resume, Pause, Progress, Success, and Failure.

+ 45 - 47
FirebaseStorage/Sources/StorageReference.swift

@@ -17,6 +17,7 @@ import Foundation
 /// `StorageReference` represents a reference to a Google Cloud Storage object. Developers can
 /// upload and download objects, as well as get/set object metadata, and delete an object at the
 /// path. See the [Cloud docs](https://cloud.google.com/storage/)  for more details.
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
 @objc(FIRStorageReference) open class StorageReference: NSObject {
   // MARK: - Public APIs
 
@@ -240,11 +241,10 @@ import Foundation
   @objc(downloadURLWithCompletion:)
   open func downloadURL(completion: @escaping ((_: URL?, _: Error?) -> Void)) {
     let fetcherService = storage.fetcherServiceForApp
-    let task = StorageGetDownloadURLTask(reference: self,
-                                         fetcherService: fetcherService,
-                                         queue: storage.dispatchQueue,
-                                         completion: completion)
-    task.enqueue()
+    StorageGetDownloadURLTask.getDownloadURLTask(reference: self,
+                                                 fetcherService: fetcherService,
+                                                 queue: storage.dispatchQueue,
+                                                 completion: completion)
   }
 
   /// Asynchronously retrieves a long lived download URL with a revokable token.
@@ -342,13 +342,12 @@ import Foundation
       items.append(contentsOf: listResult.items)
 
       if let pageToken = listResult.pageToken {
-        let nextPage = StorageListTask(reference: strongSelf,
-                                       fetcherService: fetcherService,
-                                       queue: strongSelf.storage.dispatchQueue,
-                                       pageSize: nil,
-                                       previousPageToken: pageToken,
-                                       completion: paginatedCompletion)
-        nextPage.enqueue()
+        StorageListTask.listTask(reference: strongSelf,
+                                 fetcherService: fetcherService,
+                                 queue: strongSelf.storage.dispatchQueue,
+                                 pageSize: nil,
+                                 previousPageToken: pageToken,
+                                 completion: paginatedCompletion)
       } else {
         let result = StorageListResult(withPrefixes: prefixes, items: items, pageToken: nil)
 
@@ -358,13 +357,12 @@ import Foundation
       }
     }
 
-    let task = StorageListTask(reference: self,
-                               fetcherService: fetcherService,
-                               queue: storage.dispatchQueue,
-                               pageSize: nil,
-                               previousPageToken: nil,
-                               completion: paginatedCompletion)
-    task.enqueue()
+    StorageListTask.listTask(reference: self,
+                             fetcherService: fetcherService,
+                             queue: storage.dispatchQueue,
+                             pageSize: nil,
+                             previousPageToken: nil,
+                             completion: paginatedCompletion)
   }
 
   /// Lists all items (files) and prefixes (folders) under this StorageReference.
@@ -404,13 +402,12 @@ import Foundation
       ))
     } else {
       let fetcherService = storage.fetcherServiceForApp
-      let task = StorageListTask(reference: self,
-                                 fetcherService: fetcherService,
-                                 queue: storage.dispatchQueue,
-                                 pageSize: maxResults,
-                                 previousPageToken: nil,
-                                 completion: completion)
-      task.enqueue()
+      StorageListTask.listTask(reference: self,
+                               fetcherService: fetcherService,
+                               queue: storage.dispatchQueue,
+                               pageSize: maxResults,
+                               previousPageToken: nil,
+                               completion: completion)
     }
   }
 
@@ -440,13 +437,12 @@ import Foundation
       ))
     } else {
       let fetcherService = storage.fetcherServiceForApp
-      let task = StorageListTask(reference: self,
-                                 fetcherService: fetcherService,
-                                 queue: storage.dispatchQueue,
-                                 pageSize: maxResults,
-                                 previousPageToken: pageToken,
-                                 completion: completion)
-      task.enqueue()
+      StorageListTask.listTask(reference: self,
+                               fetcherService: fetcherService,
+                               queue: storage.dispatchQueue,
+                               pageSize: maxResults,
+                               previousPageToken: pageToken,
+                               completion: completion)
     }
   }
 
@@ -458,11 +454,10 @@ import Foundation
   @objc(metadataWithCompletion:)
   open func getMetadata(completion: @escaping ((_: StorageMetadata?, _: Error?) -> Void)) {
     let fetcherService = storage.fetcherServiceForApp
-    let task = StorageGetMetadataTask(reference: self,
-                                      fetcherService: fetcherService,
-                                      queue: storage.dispatchQueue,
-                                      completion: completion)
-    task.enqueue()
+    StorageGetMetadataTask.getMetadataTask(reference: self,
+                                           fetcherService: fetcherService,
+                                           queue: storage.dispatchQueue,
+                                           completion: completion)
   }
 
   /// Retrieves metadata associated with an object at the current path.
@@ -486,12 +481,11 @@ import Foundation
   open func updateMetadata(_ metadata: StorageMetadata,
                            completion: ((_: StorageMetadata?, _: Error?) -> Void)?) {
     let fetcherService = storage.fetcherServiceForApp
-    let task = StorageUpdateMetadataTask(reference: self,
-                                         fetcherService: fetcherService,
-                                         queue: storage.dispatchQueue,
-                                         metadata: metadata,
-                                         completion: completion)
-    task.enqueue()
+    StorageUpdateMetadataTask.updateMetadataTask(reference: self,
+                                                 fetcherService: fetcherService,
+                                                 queue: storage.dispatchQueue,
+                                                 metadata: metadata,
+                                                 completion: completion)
   }
 
   /// Updates the metadata associated with an object at the current path.
@@ -514,11 +508,15 @@ import Foundation
   @objc(deleteWithCompletion:)
   open func delete(completion: ((_: Error?) -> Void)?) {
     let fetcherService = storage.fetcherServiceForApp
-    let task = StorageDeleteTask(reference: self,
+    let completionWrap = { (_: Data?, error: Error?) in
+      if let completion {
+        completion(error)
+      }
+    }
+    StorageDeleteTask.deleteTask(reference: self,
                                  fetcherService: fetcherService,
                                  queue: storage.dispatchQueue,
-                                 completion: completion)
-    task.enqueue()
+                                 completion: completionWrap)
   }
 
   /// Deletes the object at the current path.

+ 1 - 0
FirebaseStorage/Sources/StorageTask.swift

@@ -29,6 +29,7 @@ import Foundation
  * If no queue is specified, it defaults to the main queue.
  * This class is thread-safe.
  */
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
 @objc(FIRStorageTask) open class StorageTask: NSObject {
   /**
    * An immutable view of the task and associated metadata, progress, error, etc.

+ 1 - 0
FirebaseStorage/Sources/StorageTaskSnapshot.swift

@@ -19,6 +19,7 @@ import Foundation
  * A snapshot contains a task, storage reference, metadata (if it exists),
  * progress, and an error (if one occurred).
  */
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
 @objc(FIRStorageTaskSnapshot) open class StorageTaskSnapshot: NSObject {
   /**
    * The task this snapshot represents.

+ 1 - 0
FirebaseStorage/Sources/StorageUploadTask.swift

@@ -38,6 +38,7 @@ import Foundation
  * Uploads are performed on a background queue, and callbacks are raised on the developer
  * specified `callbackQueue` in Storage, or the main queue if unspecified.
  */
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
 @objc(FIRStorageUploadTask) open class StorageUploadTask: StorageObservableTask,
   StorageTaskManagement {
   /**

+ 1 - 0
FirebaseStorage/Tests/Integration/StorageIntegration.swift

@@ -204,6 +204,7 @@ class StorageResultTests: StorageIntegrationCommon {
         case let .unauthorized(bucket, object, serverError):
           XCTAssertEqual(bucket, "ios-opensource-samples.appspot.com")
           XCTAssertEqual(object, file)
+          XCTAssertNil(serverError)
           expectation.fulfill()
         default:
           XCTFail("Failed with unexpected error: \(error)")

+ 13 - 18
FirebaseStorage/Tests/Unit/StorageDeleteTests.swift

@@ -17,6 +17,7 @@ import Foundation
 import GTMSessionFetcherCore
 import XCTest
 
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
 class StorageDeleteTests: StorageTestHelpers {
   var fetcherService: GTMSessionFetcherService?
   var dispatchQueue: DispatchQueue?
@@ -54,14 +55,13 @@ class StorageDeleteTests: StorageTestHelpers {
     }
     let path = objectPath()
     let ref = StorageReference(storage: storage(), path: path)
-    let task = StorageDeleteTask(
+    StorageDeleteTask.deleteTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self
-    ) { error in
+    ) { _, error in
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 
@@ -81,14 +81,13 @@ class StorageDeleteTests: StorageTestHelpers {
     }
     let path = objectPath()
     let ref = StorageReference(storage: storage(), path: path)
-    let task = StorageDeleteTask(
+    StorageDeleteTask.deleteTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self
-    ) { error in
+    ) { _, error in
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 
@@ -105,14 +104,13 @@ class StorageDeleteTests: StorageTestHelpers {
 
     let path = objectPath()
     let ref = StorageReference(storage: storage, path: path)
-    let task = StorageDeleteTask(
+    StorageDeleteTask.deleteTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self
-    ) { error in
+    ) { _, error in
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 
@@ -122,15 +120,14 @@ class StorageDeleteTests: StorageTestHelpers {
     fetcherService!.testBlock = unauthenticatedBlock()
     let path = objectPath()
     let ref = StorageReference(storage: storage(), path: path)
-    let task = StorageDeleteTask(
+    StorageDeleteTask.deleteTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self
-    ) { error in
+    ) { _, error in
       XCTAssertEqual((error as? NSError)!.code, StorageErrorCode.unauthenticated.rawValue)
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 
@@ -140,15 +137,14 @@ class StorageDeleteTests: StorageTestHelpers {
     fetcherService!.testBlock = unauthorizedBlock()
     let path = objectPath()
     let ref = StorageReference(storage: storage(), path: path)
-    let task = StorageDeleteTask(
+    StorageDeleteTask.deleteTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self
-    ) { error in
+    ) { _, error in
       XCTAssertEqual((error as? NSError)!.code, StorageErrorCode.unauthorized.rawValue)
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 
@@ -158,15 +154,14 @@ class StorageDeleteTests: StorageTestHelpers {
     fetcherService!.testBlock = notFoundBlock()
     let path = objectPath()
     let ref = StorageReference(storage: storage(), path: path)
-    let task = StorageDeleteTask(
+    StorageDeleteTask.deleteTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self
-    ) { error in
+    ) { _, error in
       XCTAssertEqual((error as? NSError)!.code, StorageErrorCode.objectNotFound.rawValue)
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 }

+ 8 - 14
FirebaseStorage/Tests/Unit/StorageGetMetadataTests.swift

@@ -17,6 +17,7 @@ import Foundation
 import GTMSessionFetcherCore
 import XCTest
 
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
 class StorageGetMetadataTests: StorageTestHelpers {
   var fetcherService: GTMSessionFetcherService?
   var dispatchQueue: DispatchQueue?
@@ -54,14 +55,13 @@ class StorageGetMetadataTests: StorageTestHelpers {
     }
     let path = objectPath()
     let ref = StorageReference(storage: storage(), path: path)
-    let task = StorageGetMetadataTask(
+    StorageGetMetadataTask.getMetadataTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self
     ) { metadata, error in
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 
@@ -81,14 +81,13 @@ class StorageGetMetadataTests: StorageTestHelpers {
     }
     let path = objectPath()
     let ref = StorageReference(storage: storage(), path: path)
-    let task = StorageGetMetadataTask(
+    StorageGetMetadataTask.getMetadataTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self
     ) { metadata, error in
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 
@@ -105,14 +104,13 @@ class StorageGetMetadataTests: StorageTestHelpers {
 
     let path = objectPath()
     let ref = StorageReference(storage: storage, path: path)
-    let task = StorageGetMetadataTask(
+    StorageGetMetadataTask.getMetadataTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self
     ) { metadata, error in
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 
@@ -122,7 +120,7 @@ class StorageGetMetadataTests: StorageTestHelpers {
     fetcherService!.testBlock = unauthenticatedBlock()
     let path = objectPath()
     let ref = StorageReference(storage: storage(), path: path)
-    let task = StorageGetMetadataTask(
+    StorageGetMetadataTask.getMetadataTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self
@@ -130,7 +128,6 @@ class StorageGetMetadataTests: StorageTestHelpers {
       XCTAssertEqual((error as? NSError)!.code, StorageErrorCode.unauthenticated.rawValue)
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 
@@ -140,7 +137,7 @@ class StorageGetMetadataTests: StorageTestHelpers {
     fetcherService!.testBlock = unauthorizedBlock()
     let path = objectPath()
     let ref = StorageReference(storage: storage(), path: path)
-    let task = StorageGetMetadataTask(
+    StorageGetMetadataTask.getMetadataTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self
@@ -148,7 +145,6 @@ class StorageGetMetadataTests: StorageTestHelpers {
       XCTAssertEqual((error as? NSError)!.code, StorageErrorCode.unauthorized.rawValue)
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 
@@ -158,7 +154,7 @@ class StorageGetMetadataTests: StorageTestHelpers {
     fetcherService!.testBlock = notFoundBlock()
     let path = objectPath()
     let ref = StorageReference(storage: storage(), path: path)
-    let task = StorageGetMetadataTask(
+    StorageGetMetadataTask.getMetadataTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self
@@ -166,7 +162,6 @@ class StorageGetMetadataTests: StorageTestHelpers {
       XCTAssertEqual((error as? NSError)!.code, StorageErrorCode.objectNotFound.rawValue)
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 
@@ -176,7 +171,7 @@ class StorageGetMetadataTests: StorageTestHelpers {
     fetcherService!.testBlock = invalidJSONBlock()
     let path = objectPath()
     let ref = StorageReference(storage: storage(), path: path)
-    let task = StorageGetMetadataTask(
+    StorageGetMetadataTask.getMetadataTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self
@@ -186,7 +181,6 @@ class StorageGetMetadataTests: StorageTestHelpers {
       XCTAssertEqual(nsError.code, StorageErrorCode.unknown.rawValue)
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 }

+ 9 - 18
FirebaseStorage/Tests/Unit/StorageListTests.swift

@@ -17,6 +17,7 @@ import Foundation
 import GTMSessionFetcherCore
 import XCTest
 
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
 class StorageListTests: StorageTestHelpers {
   var fetcherService: GTMSessionFetcherService?
   var dispatchQueue: DispatchQueue?
@@ -106,7 +107,7 @@ class StorageListTests: StorageTestHelpers {
 
     let path = objectPath()
     let ref = StorageReference(storage: storage(), path: path)
-    let task = StorageListTask(
+    StorageListTask.listTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self,
@@ -115,7 +116,6 @@ class StorageListTests: StorageTestHelpers {
     ) { result, error in
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 
@@ -151,7 +151,7 @@ class StorageListTests: StorageTestHelpers {
 
     let path = objectPath()
     let ref = StorageReference(storage: storage, path: path)
-    let task = StorageListTask(
+    StorageListTask.listTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self,
@@ -161,9 +161,6 @@ class StorageListTests: StorageTestHelpers {
       XCTAssertNil(error)
       expectation.fulfill()
     }
-
-    task.enqueue()
-
     waitForExpectation(test: self)
   }
 
@@ -198,7 +195,7 @@ class StorageListTests: StorageTestHelpers {
 
     let path = objectPath()
     let ref = StorageReference(storage: storage(), path: path)
-    let task = StorageListTask(
+    StorageListTask.listTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self,
@@ -207,7 +204,6 @@ class StorageListTests: StorageTestHelpers {
     ) { result, error in
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 
@@ -240,7 +236,7 @@ class StorageListTests: StorageTestHelpers {
 
     let storage = storage()
     let ref = storage.reference(withPath: "+foo")
-    let task = StorageListTask(
+    StorageListTask.listTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self,
@@ -249,7 +245,6 @@ class StorageListTests: StorageTestHelpers {
     ) { result, error in
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 
@@ -287,7 +282,7 @@ class StorageListTests: StorageTestHelpers {
 
     let storage = storage()
     let ref = storage.reference(withPath: "object")
-    let task = StorageListTask(
+    StorageListTask.listTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self,
@@ -306,7 +301,6 @@ class StorageListTests: StorageTestHelpers {
 
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 
@@ -326,7 +320,7 @@ class StorageListTests: StorageTestHelpers {
 
     let storage = storage()
     let ref = storage.reference(withPath: "object")
-    let task = StorageListTask(
+    StorageListTask.listTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self,
@@ -335,13 +329,10 @@ class StorageListTests: StorageTestHelpers {
     ) { result, error in
       XCTAssertNotNil(error)
       XCTAssertNil(result)
-
-      XCTAssertEqual(error!.domain, "FIRStorageErrorDomain")
-      XCTAssertEqual(error!.code, StorageErrorCode.objectNotFound.rawValue)
-
+      XCTAssertEqual((error as? NSError)!.domain, "FIRStorageErrorDomain")
+      XCTAssertEqual((error as? NSError)!.code, StorageErrorCode.objectNotFound.rawValue)
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 }

+ 6 - 15
FirebaseStorage/Tests/Unit/StorageMetadataTests.swift

@@ -111,11 +111,8 @@ class StorageMetadataTests: StorageTestHelpers {
   func testInitializeEmptyDownloadURL() {
     let metaDict = ["bucket": "bucket", "name": "/path/to/object"]
     let rootReference = rootReference()
-    let task = StorageGetDownloadURLTask(reference: rootReference,
-                                         fetcherService: GTMSessionFetcherService(),
-                                         queue: DispatchQueue.main,
-                                         completion: nil)
-    let actualURL = task.downloadURLFromMetadataDictionary(metaDict)
+    let actualURL = StorageGetDownloadURLTask.downloadURLFromMetadataDictionary(metaDict,
+                                                                                rootReference)
     XCTAssertNil(actualURL)
   }
 
@@ -130,11 +127,8 @@ class StorageMetadataTests: StorageTestHelpers {
     let escapedPath = StorageUtils.GCSEscapedString(metaDict["name"]!)
     let expectedURL =
       "https://firebasestorage.googleapis.com:443/v0/b/bucket/o/\(escapedPath)?alt=media&token=12345"
-    let task = StorageGetDownloadURLTask(reference: rootReference,
-                                         fetcherService: GTMSessionFetcherService(),
-                                         queue: DispatchQueue.main,
-                                         completion: nil)
-    let actualURL = task.downloadURLFromMetadataDictionary(metaDict)
+    let actualURL = StorageGetDownloadURLTask.downloadURLFromMetadataDictionary(metaDict,
+                                                                                rootReference)
     XCTAssertEqual(actualURL?.absoluteString, expectedURL)
   }
 
@@ -151,11 +145,8 @@ class StorageMetadataTests: StorageTestHelpers {
     let escapedPath = StorageUtils.GCSEscapedString("path/to/object")
     let expectedURL =
       "https://firebasestorage.googleapis.com:443/v0/b/bucket/o/\(escapedPath)?alt=media&token=12345"
-    let task = StorageGetDownloadURLTask(reference: rootReference,
-                                         fetcherService: GTMSessionFetcherService(),
-                                         queue: DispatchQueue.main,
-                                         completion: nil)
-    let actualURL = task.downloadURLFromMetadataDictionary(metaDict)
+    let actualURL = StorageGetDownloadURLTask.downloadURLFromMetadataDictionary(metaDict,
+                                                                                rootReference)
     XCTAssertEqual(actualURL?.absoluteString, expectedURL)
   }
 

+ 1 - 1
FirebaseStorage/Tests/Unit/StorageReferenceTests.swift

@@ -220,7 +220,7 @@ class StorageReferenceTests: XCTestCase {
         XCTFail("Unexpected success.", file: #file, line: #line)
       case let .failure(error):
         switch error {
-        case let StorageError.unknown(message, serverError):
+        case let StorageError.unknown(message, _):
           let expectedDescription = "File at URL: \(dummyFileURL.absoluteString) " +
             "is not reachable. Ensure file URL is not a directory, symbolic link, or invalid url."
           XCTAssertEqual(expectedDescription, message)

+ 7 - 14
FirebaseStorage/Tests/Unit/StorageUpdateMetadataTests.swift

@@ -50,7 +50,7 @@ class StorageUpdateMetadataTests: StorageTestHelpers {
     }
     let path = objectPath()
     let ref = StorageReference(storage: storage(), path: path)
-    let task = StorageUpdateMetadataTask(
+    StorageUpdateMetadataTask.updateMetadataTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self,
@@ -58,7 +58,6 @@ class StorageUpdateMetadataTests: StorageTestHelpers {
     ) { metadata, error in
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 
@@ -67,7 +66,7 @@ class StorageUpdateMetadataTests: StorageTestHelpers {
     fetcherService!.testBlock = successBlock(withMetadata: metadata)
     let path = objectPath()
     let ref = StorageReference(storage: storage(), path: path)
-    let task = StorageUpdateMetadataTask(
+    StorageUpdateMetadataTask.updateMetadataTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self,
@@ -78,7 +77,6 @@ class StorageUpdateMetadataTests: StorageTestHelpers {
       XCTAssertEqual(self.metadata?.name, metadata?.name)
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 
@@ -95,7 +93,7 @@ class StorageUpdateMetadataTests: StorageTestHelpers {
 
     let path = objectPath()
     let ref = StorageReference(storage: storage, path: path)
-    let task = StorageUpdateMetadataTask(
+    StorageUpdateMetadataTask.updateMetadataTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self,
@@ -103,7 +101,6 @@ class StorageUpdateMetadataTests: StorageTestHelpers {
     ) { metadata, error in
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 
@@ -113,7 +110,7 @@ class StorageUpdateMetadataTests: StorageTestHelpers {
     fetcherService!.testBlock = unauthenticatedBlock()
     let path = objectPath()
     let ref = StorageReference(storage: storage(), path: path)
-    let task = StorageUpdateMetadataTask(
+    StorageUpdateMetadataTask.updateMetadataTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self,
@@ -122,7 +119,6 @@ class StorageUpdateMetadataTests: StorageTestHelpers {
       XCTAssertEqual((error as? NSError)!.code, StorageErrorCode.unauthenticated.rawValue)
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 
@@ -132,7 +128,7 @@ class StorageUpdateMetadataTests: StorageTestHelpers {
     fetcherService!.testBlock = unauthorizedBlock()
     let path = objectPath()
     let ref = StorageReference(storage: storage(), path: path)
-    let task = StorageUpdateMetadataTask(
+    StorageUpdateMetadataTask.updateMetadataTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self,
@@ -141,7 +137,6 @@ class StorageUpdateMetadataTests: StorageTestHelpers {
       XCTAssertEqual((error as? NSError)!.code, StorageErrorCode.unauthorized.rawValue)
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 
@@ -151,7 +146,7 @@ class StorageUpdateMetadataTests: StorageTestHelpers {
     fetcherService!.testBlock = notFoundBlock()
     let path = objectPath()
     let ref = StorageReference(storage: storage(), path: path)
-    let task = StorageUpdateMetadataTask(
+    StorageUpdateMetadataTask.updateMetadataTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self,
@@ -160,7 +155,6 @@ class StorageUpdateMetadataTests: StorageTestHelpers {
       XCTAssertEqual((error as? NSError)!.code, StorageErrorCode.objectNotFound.rawValue)
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 
@@ -170,7 +164,7 @@ class StorageUpdateMetadataTests: StorageTestHelpers {
     fetcherService!.testBlock = invalidJSONBlock()
     let path = objectPath()
     let ref = StorageReference(storage: storage(), path: path)
-    let task = StorageUpdateMetadataTask(
+    StorageUpdateMetadataTask.updateMetadataTask(
       reference: ref,
       fetcherService: fetcherService!.self,
       queue: dispatchQueue!.self,
@@ -179,7 +173,6 @@ class StorageUpdateMetadataTests: StorageTestHelpers {
       XCTAssertEqual((error as? NSError)!.code, StorageErrorCode.unknown.rawValue)
       expectation.fulfill()
     }
-    task.enqueue()
     waitForExpectation(test: self)
   }
 }