SwiftUI 网络开发大全之iOS 使用 URLSession 联网

URLSession 是 Apple 强大的 API,我们可以使用它在 iOS 应用程序中下载和上传数据

URLSession 是一个对象,负责处理 iOS 中的 http 和 https 请求。它要求 URLSessionConfiguration 对象向 Web 服务发送和接收请求。下面是一个关于 URLSession 类的简单依赖关系图:

image.png

URLSession 有一个名为 URLSessionConfiguration 的依赖项。我们使用它来配置 http 请求属性,如缓存策略、会话类型、http 标头、超时等。当您定义会话配置时,您有三个选择:

  • .default
    它将创建使用磁盘持久化全局缓存、凭据和 cookie 存储对象的默认配置对象。
  • .ephemeral
    类似于 .default 配置,除非会话相关的数据会保存在内存中。
  • .background
    它将为下载和上传任务创建一个后台会话,它可以恢复或取消,并且可以在应用程序终止或终止后继续任务。

在上图中,您可以看到 URLSessionDataTask。它是一个抽象的对象,代表现实生活中请求的真实工作。URLSession 可以创建多个任务来处理一些用例,例如获取数据、下载和上传文件。共有三种类型的任务:

  • URLSessionDataTask
    我们可以使用此任务通过 GET 方法从 Web 服务检索数据。
  • URLSessionUploadTask
    我们可以使用此任务使用 POST 或 PUT 方法将数据上传到 Web 服务。
  • URLSessionDownloadTask
    我们可以使用此任务从 Web 服务下载文件并将其保存到临时本地存储。

通常,我们可以通过两种方式从 URLSession 中获取请求的结果。首先,我们可以对结果使用闭包完成处理程序,无论是否成功。其次,如果我们在定义 URLSession 对象时设置了委托属性,我们就可以使用委托。
以下是有关如何使用 URLSession 从 Web 服务获取数据的简单示例:

final class ExampleNetworkService {

  private let urlSession: URLSession
  private var dataTask: URLSessionDataTask?

  init(sessionConfiguration: URLSessionConfiguration) {
    self.urlSession = URLSession(configuration: sessionConfiguration)
  }

  func request(url: String, query: String?, httpMethod: String, completion: @escaping (Result<Data, Error>) -> Void) {

    dataTask?.cancel()

    guard var urlComponents = URLComponents(string: url) else {
      return
    }

    if let urlQuery = query {
      urlComponents.query = urlQuery
    }

    guard let validUrl = urlComponents.url else {
      return
    }

    var urlRequest = URLRequest(url: validUrl)
    urlRequest.httpMethod = httpMethod

    dataTask = urlSession.dataTask(with: urlRequest, completionHandler: { [weak self] (data: Data?, response: URLResponse?, error: Error?) in
      do {
        self?.dataTask = nil
      }

      guard error == nil else {
        let error = NSError(domain: "invalid endpoint", code: 900013, userInfo: nil)
        completion(.failure(error))
        return
      }

      if let data = data, let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 {
        completion(.success(data))
        return
      } else {
        let error = NSError(domain: "invalid response", code: 900014, userInfo: nil)
        completion(.failure(error))
      }
    })

    dataTask?.resume()
  }
}

下面是上面示例代码中发生的事情:
在 ExampleNetworkService 类中,它有两个属性,即 URLSession 和 URLSessionDataTask 实例。在初始化程序中,我们将会话配置定义为参数。通过这个实现,我们可以在使用 – ExampleNetworkService 类时注入任何我们想要的会话配置。
我们有一个叫做 request 的函数。它需要一些参数,如 url、query、http 方法和使用 Result 类型的完成处理程序。在函数内部,它基本上验证 url、设置查询并创建 url 请求对象。
定义好url请求对象之后,我们就可以从url会话中执行网络请求,并将其传递给我们之前已经定义好的数据任务。最后,我们要求数据任务恢复,因为它基本上是第一次数据任务的状态终止。
这很简单吧?您只需通过 url 请求设置 http 方法,我们就可以开始了。然后让我们配置如何使用 URLSessionDownloadTask 从 iTunes API 下载一些音乐。首先,我们创建了一个新类型来表示音乐对象。

class Music {
  let artist: String
  let index: Int
  let name: String
  let previewURL: URL
  var isDownloaded = false

  init(artist: String, index: Int, name: String, previewURL: URL) {
    self.artist = artist
    self.index = index
    self.name = name
    self.previewURL = previewURL
  }
}

Music 类型有一些属性,如艺术家、名称和预览 URL。index 属性是我们以后处理 table view 时可以使用的辅助属性。另一个属性是 isDownloaded,默认值为 false,表示音乐是否已经下载。
接下来,我们创建代表下载对象的新类型。这对我们以后管理下载状态有很大帮助。

class Download {
  var isDownloading = false
  var progress: Float = 0
  var resumeData: Data?

  weak var downloadTask: URLSessionDownloadTask?
  let music: Music

  init(music: Music) {
    self.music = music
  }
}

Download 类有一些辅助属性,如 isDownloading,这表示音乐是否正在下载。如果您需要在 UI 中显示下载进度,progress 属性也是辅助属性。当您暂停下载并想要继续下载时,会使用恢复数据,我们稍后可以从 URLSession 对象处理它。然后 Download 类具有名为 downloadTask 的属性,它是 URLSessionDownloadTask 的实例,我们可以使用它从 Web 服务下载文件的特定任务。
下一步是创建其中包含 URLSession 的下载服务对象。

final class ExampleDownloadService {

  var activeDownloads: [URL : Download] = [:]

  private let downloadSession: URLSession

  init(sessionConfiguration: URLSessionConfiguration, delegate: URLSessionDelegate) {
    self.downloadSession = URLSession(
      configuration: sessionConfiguration,
      delegate: delegate,
      delegateQueue: nil
    )
  }
}

在 ExampleDownloadService 类中,它具有名为 activeDownloads 字典的属性。我们可以使用它来跟踪当前时间是否有任何活动下载。接下来,我们定义下载会话,它是 URLSession 的一个实例。在初始化器中,我们有两个参数,即会话配置和 URLSessionDelegate。有了这个实现我们就可以根据自己的需要注入session配置了,这次我们需要实现URLSessionDelegate来处理后面下载任务的完成。
现在我们的下载服务已经准备好了,我们可以继续执行服务内部的下载任务了。在服务内部,我们有四个用例,分别是启动、取消、暂停和恢复下载。

func startDownload(_ music: Music) {
    let download = Download(music: music)
    download.downloadTask = downloadSession.downloadTask(with: music.previewURL)
    download.downloadTask?.resume()
    download.isDownloading = true
    activeDownloads[music.previewURL] = download
}

func cancelDownload(_ music: Music) {
    guard let download = activeDownloads[music.previewURL] else {
      return
    }

    download.downloadTask?.cancel()
    activeDownloads[music.previewURL] = nil
    download.isDownloading = false
}

func pauseDownload(_ music: Music) {
    guard let download = activeDownloads[music.previewURL] else {
      return
    }

    download.downloadTask?.cancel(byProducingResumeData: { (resumeData: Data?) in
      download.resumeData = resumeData
    })

    download.isDownloading = false
}

func resumeDownload(_ music: Music) {
    guard let download = activeDownloads[music.previewURL] else {
      return
    }

    if let resumeData = download.resumeData {
      download.downloadTask = downloadSession.downloadTask(withResumeData: resumeData)
    } else {
      download.downloadTask = downloadSession.downloadTask(with: music.previewURL)
    }

    download.downloadTask?.resume()
    download.isDownloading = true
}

当您要开始下载或取消下载时,这非常简单。调用 可以启动resume()下载任务,调用cancel()函数可以取消下载任务。
但是当你想暂停下载时,它有点不同。这意味着您想稍后继续下载,对吗?幸运的是,这很简单。您可以调用另一个取消功能,这时候您要取消下载还需要恢复数据。恢复数据包含继续下载所需的所有信息。将简历数据保存在下载对象中。
然后如果你想继续下载,你可以从下载对象中提取简历数据。如果存在,那么您可以启动下载会话并将恢复数据传递给提供的参数。否则,只需从头开始下载。
好的,这是很长的解释。接下来,为了处理下载任务,我们需要实现 URLSessionDelegate 方法。

extension ViewController: URLSessionDelegate {
  func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
     // handle finished download here
  }

  func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
    // when you need to show the progress download, implement this delegate method 
  }

  func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
     // implement this delegate method if you use background session configuration
  }

您需要的一切都已在 URLSessionDelegate 中提供。下载是否完成,或者您是否需要配置下载进度,或者甚至当您使用后台会话配置定义后台传输时。
挑战
首先,恭喜你一直关注这篇文章到最后。正如你所看到的,这篇文章只是解释了关于 URLSession 的详细概念,缺乏实际的实现:]。如果你接受它,你的使命就是创建简单的 iOS 应用程序来实现你关于 URLSession 的所有知识。你可以玩另一个任务比如上传任务,也可以尝试设置会话配置,比如超时、缓存策略等。感谢一直支持我写一篇关于iOS开发的文章。如果您有任何反馈或建议,请告诉我。谢谢你,并在当前的大流行情况下保持安全:]。


精品教程推荐


加入我们一起学习SwiftUI

QQ:3365059189
SwiftUI技术交流QQ群:518696470
教程网站:www.openswiftui.com

发表回复