Swift 生成一个 Http Request
apple 发布于 2024-07-21

URLSession实例是应用程序执行的请求的管理器或协调器。请求被称为任务,URLSessionTask的一个实例。您永远不会直接使用URLSessionTask类。Foundation定义了许多URLSessionTask子类。每个子类都有一个特定的目标,例如下载或上传数据。

选项1:完成处理程序

创建数据任务

让我们使用URLSession API来执行HTTP请求。目标是获取图像的数据。在开始之前,我们需要定义远程映像的URL。

下一步是创建一个数据任务,即URLSessionDataTask类的实例。数据任务始终与URLSession实例相关联。为了简单起见,我们通过其共享类属性向URLSession类请求共享URLSession实例(单例)。

URLSession.shared

然后,我们要求共享URLSession实例通过调用dataTask(with:completionHandler:)方法创建数据任务。此方法返回一个URLSessionDataTask实例,并接受两个参数,一个URL对象和一个完成处理程序。完成处理程序是一个闭包,在数据任务成功或失败完成时执行。完成处理程序接受三个参数,可选的Data对象、可选的URLResponse对象和可选的Error对象。

import UIKit
let url = URL(string: "https://bit.ly/2LMtByx")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
    }

数据任务失败或成功。如果数据任务失败,则错误有一个值。如果数据任务成功,则数据和响应具有值。我们目前对URLResponse对象不感兴趣。我们安全地解压缩Data对象并使用它来创建UIImage实例。如果data等于nil,则HTTP请求失败,我们将错误值打印到控制台。

import UIKit
let url = URL(string: "https://bit.ly/2LMtByx")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
    if let data = data {
        let image = UIImage(data: data)
    } else if let error = error {
        print("HTTP Request Failed \(error)")
    }}

即使我们创建了一个数据任务,HTTP请求也不会被执行。我们需要对URLSessionDataTask实例调用resume()来执行它。

import UIKit
let url = URL(string: "https://bit.ly/2LMtByx")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
    if let data = data {
        let image = UIImage(data: data)
    } else if let error = error {
        print("HTTP Request Failed \(error)")
    }}

task.resume()

如果您运行游乐场的内容并等待几秒钟,您应该会看到HTTP请求的结果出现在右侧的结果面板中。UIImage实例的尺寸显示在结果面板中。控制台上没有打印错误,这意味着HTTP请求已成功执行。

创建请求

我们通过将URL对象传递给dataTask(with:completionHandler:)方法来创建数据任务。这工作得很好,但它没有提供太多的灵活性。URLSession类定义了另一个接受URLRequest对象的方法。顾名思义,URLRequest结构封装了URL会话执行HTTP请求所需的信息。让我给你演示一下这是如何工作的。

我们通过将URL对象传递给初始化器来创建URLRequest对象,并将结果存储在一个名为request的变量中。我们稍后修改URLRequest对象,因此使用var而不是let。

var request = URLRequest(url: url)

我们将URLRequest对象传递给dataTask(with:completionHandler:)方法。该方法的名称与我们之前使用的方法相似。区别在于它接受URLRequest对象而不是URL对象。

import UIKit
let url = URL(string: "https://bit.ly/2LMtByx")!
var request = URLRequest(url: url)
let task = URLSession.shared.dataTask(with: url) { data, response, error in
    if let data = data {
        let image = UIImage(data: data)
    } else if let error = error {
        print("HTTP Request Failed \(error)")
    }}

task.resume()

执行操场上的内容。请注意,结果是相同的。您可能想知道我们使用URLRequest能得到什么。URLRequest对象允许我们配置URL会话执行的HTTP请求。我们可以通过httpMethod属性设置HTTP方法。在这个例子中,这是不必要的,因为它默认为GET。

request.httpMethod = "GET"

我们还可以定义请求的HTTP标头字段。如果需要覆盖默认的HTTP标头字段,则设置allHTTPHeaderFields属性,即[String:String]类型的字典。如果您需要处理身份验证,这一点也很重要。在本例中,我们添加了一个HTTP头字段,以便将API密钥传递给正在通信的服务器。

request.allHTTPHeaderFields = [
    "X-API-Key": "123456789"]

您还可以通过调用setValue(_:forHTTPHeaderField:)方法来设置请求的HTTP标头字段。这是一个可变方法,用于设置给定HTTP标头字段的值。

request.setValue("application/png", forHTTPHeaderField: "Content-Type")

使用URLSession请求JSON

获取JSON是另一个常见的例子。这并不比获取远程图像的数据更困难。我们更新URL对象,并将Content-Type HTTP标头字段的值设置为application/json。

import UIKit
let url = URL(string: "https://bit.ly/3sspdFO")!
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")

我们有几个选项来解码响应。除非你有充分的理由,否则我不建议使用JSONSerialization类。首选和推荐的方法是可解码协议与JSONDecoder类相结合。

我们定义了一个名为Book的结构体,它符合可解码协议。Book结构定义了两个属性,String类型的title和String类型的author。

import UIKit
struct Book: Decodable {

    let title: String
    let author: String
}
let url = URL(string: "https://bit.ly/3sspdFO")!
var request = URLRequest(url: url)

request.setValue(
    "application/json",
    forHTTPHeaderField: "Content-Type")

在完成处理程序中,我们创建了一个JSONDecoder实例并调用decode(_:from:)方法,从提供的JSON对象传递要解码的值的类型,并将要解码的JSON对象作为Data对象传递。我们将结果打印到控制台。执行游乐场的内容以查看结果。

let task = URLSession.shared.dataTask(with: request) { data, response, error in
    if let data = data {
        if let books = try? JSONDecoder().decode([Book].self, from: data) {
            print(books)
        } else {
            print("Invalid Response")
        }
    } else if let error = error {
        print("HTTP Request Failed \(error)")
    }}

task.resume()

选项2:Swift并发

当请求成功或失败完成时,我们传递给dataTask(with:completionHandler:)方法的完成处理程序会被调用。这很好,但有一个更优雅、更现代的API可以利用Swift并发。

执行HTTP请求是一个异步操作,我们在Task中执行。在Task的主体中,我们调用共享URLSession实例上的data(for:)方法。data(for:)方法接受URLRequest对象。这是一个through方法,因此我们将其封装在do-catch语句中。我们在方法调用前加上wait关键字,因为该方法是异步的。

Task {
    do {
        let (data, _) = try await URLSession.shared.data(for: request)
    } catch {
        print("Failed to Send POST Request \(error)")
    }}

data(for:)方法返回一个包含两个值的元组,一个data对象和一个URLResponse对象。我们只对Data对象感兴趣。如前所述,我们在JSONDecoder实例的帮助下将Data对象转换为Book对象数组。

Task {
    do {
        let (data, _) = try await URLSession.shared.data(for: request)

        if let books = try? JSONDecoder().decode([Book].self, from: data) {
            print(books)
        } else {
            print("Invalid Response")
        }
    } catch {
        print("Failed to Send POST Request \(error)")
    }}

Swift并发有几个有趣的好处。首先,您可以从上到下阅读我们编写的代码。这使得代码更容易理解和推理。其次,错误处理内置于Swift并发中。我们不需要打开可选的Error对象来判断请求是否成功。如果请求失败,则抛出错误。简单。正确的第三,元组的值不是可选的。如果GET请求成功,那么我们保证可以访问Data对象和URLResponse对象。

选项3:使用Combine的响应式编程

URLSession API与苹果的反应式框架Combine巧妙地集成在一起。我们可以向发出GET请求响应的发布者询问共享URLSession实例。如果请求失败,则发布者终止或以错误完成。

URLSession.shared.dataTaskPublisher(for: request)

我们通过调用sink(receiveCompletion:receiveValue:)方法来订阅dataTaskPublisher(for:)方法返回的发布者。该方法接受两个参数,一个是发布者完成时调用的完成处理程序,另一个是请求响应可用时调用的值处理程序。我们可以通过值处理程序访问Data和URLResponse对象。

let cancellable = URLSession.shared.dataTaskPublisher(for: request)
    .sink(receiveCompletion: { completion in
        switch completion {
        case .finished:
            ()
        case .failure(let error):
            print("Failed to Send GET Request \(error)")
        }
    }, receiveValue: { data, _ in
        if let books = try? JSONDecoder().decode([Book].self, from: data) {
            print(books)
        } else {
            print("Invalid Response")
        }
    })

sink(receiveCompletion:receiveValue:)方法返回AnyCancelable。我们需要保留AnyCancelable,直到请求完成。因此,我们将对AnyCancelable的引用存储在一个名为cancelable的常量中。

有待深入

在本教程中,我们研究了三个API,以使用URLSession API在Swift中执行HTTP请求。我希望你同意这不是火箭科学。URLSession API易于使用,如果您了解更多关于API的信息,它将灵活而强大。我们在本教程中只触及了表面。如果您想了解如何使用URLSession API执行PUT或POST请求,请参阅Swift中的How to Make a POST Request。

 

apple
关注 私信
文章
13
关注
0
粉丝
0