【译】[SwiftUI 100 天] Cupcake Corner - part2

2020/4/7 23:01:28

本文主要是介绍【译】[SwiftUI 100 天] Cupcake Corner - part2,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

译自 Sending and receiving Codable data with URLSession and SwiftUI
更多内容,欢迎关注公众号 「Swift花园」
喜欢文章?不如来个 🔺💛➕三连?关注专栏,关注我 🚀🚀🚀

用 URLSession 和 SwiftUI 发送和接收 Codable 数据

iOS 为我们提供了用于从网络发送和接收数据的内置工具,如果把它与Codable支持结合使用,则可以将 Swift 对象转换为 JSON 进行发送,然后再接收回 JSON 并转换回 Swift 对象。更好的是,当请求完成时,我们可以立即将数据赋给 SwiftUI 视图中的属性,从而触发我们的用户界面更新。

为了演示这一点,我们要从 Apple 的 iTunes API 中加载一些示例音乐的 JSON 数据,然后显示在 SwiftUI 的List中。Apple 的数据包含很多信息,我们要削减成两个类型:一个存储曲目 ID ,名称和所属专辑的Result,和一个存储数组结果的Response

从下面的代码开始:

struct Response: Codable {
    var results: [Result]
}

struct Result: Codable {
    var trackId: Int
    var trackName: String
    var collectionName: String
}复制代码

我们现在可以编写一个简单的ContentView来显示结果数组:

struct ContentView: View {
    @State private var results = [Result]()

    var body: some View {
        List(results, id: \.trackId) { item in
            VStack(alignment: .leading) {
                Text(item.trackName)
                    .font(.headline)
                Text(item.collectionName)
            }
        }
    }
}复制代码

最开始不会显示任何内容,因为results数组是空的。这里是我们进行网络调用的地方:我们将要求 iTunes API 给我们发送一个泰勒·斯威夫特的所有歌曲的清单,然后用JSONDecoder把结果转换成一个Result实例的数组。

为了便于理解,让我们分几个阶段进行编程。首先,是基本的方法 —— 把下面的存根添加到ContentView结构体中:

func loadData() {

}复制代码

我们希望在显示列表后立即执行它,因此,可以把下面这个 modifier 添加到List

.onAppear(perform: loadData)复制代码

loadData()内部,我们需要完成四个步骤:

  1. 创建我们要读取的 URL。
  2. URLRequest 包装 URL,以便我们可以配置访问 URL 的方式。
  3. 基于这个 URL 请求创建并启动网络任务。
  4. 处理网络任务的结果。

我们将从 URL 开始,一步一步添加这些内容。这需要用到一个精确的格式:“itunes.apple.com”,后面跟着一系列参数 —— 如果你对 ”iTunes Search API“ 进行搜索,可以找到完整的参数集。在我们的案例中,我们要使用搜索词 “Taylor Swift” 和实体 “song”。现在,把下面的代码添加到loadData()中:

guard let url = URL(string: "https://itunes.apple.com/search?term=taylor+swift&entity=song") else {
    print("Invalid URL")
    return
}复制代码

接下来,我们需要把URL包进一个URLRequest中。再次说明,URLRequest是我们添加不同的自定义项以控制 URL 加载方式的地方,但是在这里我们不需要任何内容,只有一行代码 —— 把它添加到loadData()接下来的地方:

let request = URLRequest(url: url)复制代码

第三步是使用我们刚创建的URLRequest创建并启动网络任务。当你第一次看到它的时候,它看起来像是一个很奇怪的方法,而且它有一个特别常见的“陷阱” —— 你可能会在长达几年的时间里一次又一次地犯下错误。

我先向你展示代码,然后解释它做了什么 —— 把下面的代码添加到loadData()

URLSession.shared.dataTask(with: request) { data, response, error in
    // 第四步
}.resume()复制代码

URLSession是负责管理网络请求的 iOS 类。如果你愿意,可以创建自己的会话,但普遍的做法是使用 iOS 创建给我们使用的shared会话 —— 除非你需要某些特定的行为,否则使用这个共享会话就可以了。

然后,我们在这个共享会话上调用dataTask(with:),它从URLRequest中创建一个网络任务,并在任务完成时运行一个闭包。在我们的代码中,这是用拖尾闭包语法提供的,你可以看到它接受三个参数:

  • data 是从请求返回的数据。
  • response 是数据的描述,它可能包含数据的类型,数据量,是否有状态码,等等。
  • error 是出现的错误。

狡猾之处在于其中的一些属性是互斥的,我的意思是,如果发生error,就不会设置data,如果返回data,则不会设置error。之所以存在这种奇怪的状态,是因为URLSessionAPI 是在 Swift 出现之前创建的,没有更好的方法来表示这种二选一的状态。

注意到我们立即在任务上调用resume()的方式了吗?这就是陷阱 —— 是你会一次又一次忘记的事情。没有它,这个请求将什么都不做,而你会盯着一个空白的屏幕。但是使用它,请求会立即开始,控制权移交到系统 —— 它会自动在后台运行,即使我们的方法结束,也不会被破坏。

当请求完成,无论成功与否,第四步会开始生效 —— 这就是数据任务中的闭包,它负责对数据或错误进行处理。在我们的案例中,我们会检查数据是否被设置,如果是则尝试将其解码为我们的Response结构体的实例,因为这正是 iTunes API 发送回来的数据。我们实际上并不想要整个响应,而只是想要其中的结果数组,以便我们的List将它们全部显示出来。

不过,这里还有另一个要点:URLSession会在后台自动运行,这意味着它的完成关闭闭包也会运行在后台。所谓“后台”,是指技术上称为“后台线程”的东西 —— 与我们程序的其余部分同时运行的独立代码段。这意味着,网络请求甚至可以运行几秒钟,而不会阻断我们与 UI 的交互。

iOS 喜欢让它的所有用户界面工作都在“主线程”(程序启动时的线程)上完成。这个机制会断绝两段代码同时操作用户界面的可能性,因为如果所有与 UI 相关的工作都在主线程上进行,那么冲突不可能发生。

我们希望将视图的results属性修改为通过 iTunes API 下载的内容,然后反过来更新我们的用户界面。在后台线程上做这件事可能也工作地很好,因为SwiftUI 超级聪明。但老实说,我们不必冒险。在后台获取数据,在后台解码 JSON,然后在主线程实际更新属性,是一个更好的主意。这样可以避免任何潜在的问题。

iOS 提供了一种非常特殊的方式,用来将工作发送到主线程:DispatchQueue.main.async()。它接收一个执行工作的闭包,然后将其发送到主线程以执行。从名称中可以看出,实际上发生的事情是它被加入一个队列 —— 一个等待执行的长队伍。“async” 的部分是“异步”的缩写,表示我们自己的后台工作不会等待闭包的运行;我们只是把它添加到队列,然后继续后台的工作。

因此,用下面这段最后的代码替换// 第四步的注释:

if let data = data {
    if let decodedResponse = try? JSONDecoder().decode(Response.self, from: data) {
        // 我们已经拿好数据 —— 回到主线程
        DispatchQueue.main.async {
            // 更新我们的 UI
            self.results = decodedResponse.results
        }

        // 一切顺利,可以退出
        return
    }
}

// 如果我们到了这里,说明出现问题
print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")复制代码

最后一行的print()使用了可选链和空合运算符,如果存在错误则打印出错误,否则给出一个通用错误。

如果现在运行代码,你应该会在短暂的停顿后,看到 Taylor Swift 歌曲的列表 —— 考虑到最终结果的效果,我们的代码并不多。

在项目稍后的部分,我们将研究如何自定义URLRequest,以便你可以发送Codable数据,但目前已经足够了 —— 请将 ContentView.swift 还原回原始状态,以便我们开始工作。

我的公众号 这里有Swift及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~





这篇关于【译】[SwiftUI 100 天] Cupcake Corner - part2的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程