专栏首页韦弦的偶尔分享SwiftUI:使用 URLSession 发送和接收 Codable 数据

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

iOS为我们提供了从互联网发送和接收数据的内置工具,如果我们将其 Codable 持结合起来,那么就可以将 Swift 对象转换为 JSON 进行发送,然后将接收回来的 JSON 转换为 Swift 对象。更好的是,当请求完成时,我们可以立即将其数据分配给 SwiftUI 视图中的属性,从而导致用户界面更新。

为了演示这一点,我们可以从苹果的iTunes API中加载一些示例音乐JSON数据,并将其全部显示在一个SwiftUI List中。苹果的数据包含很多信息,但我们将把它缩减为两种类型:一个Result将存储一个曲目ID、名称和它所属的专辑,一个响应将存储一系列结果。

所以,从这段代码开始:

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向我们发送Taylor Swift的所有歌曲的列表,然后使用JSONDecoder 将这些结果转换为一组 Result 实例。

为了更容易理解,让我们分几个阶段来写。首先,这里是基本方法——请将其添加到ContentView结构中:

func loadData() {

}

我们希望在显示列表时立即运行该操作,因此您应该将此修饰符添加到List中:

.onAppear(perform: loadData)

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

  1. 创建我们要读取的URL。
  2. 将其包装在URLRequest中,这允许我们配置如何访问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。同样,在这里,我们将添加不同的自定义项来控制加载URL的方式,但在这里我们不需要任何东西,因此这只是一行代码——接下来将其添加到loadData()中:

let request = URLRequest(url: url)

第3步是使用我们刚才发出的URLRequest创建并启动一个网络任务。当你第一次看到它的时候,你会觉得这是一个相当奇怪的方法,它有一个特别常见的“陷阱”——一个你会犯一次又一次的错误,而且可能几年后还会犯。

我将首先向您展示代码,然后解释它的作用——将下方代码添加到loadData()

URLSession.shared.dataTask(with: request) { data, response, error in
    // step 4
}.resume()

URLSession是负责管理网络请求的iOS类。如果愿意,您可以创建自己的会话,但通常使用iOS创建的共享会话供我们使用,除非您需要某些特定行为,否则使用共享会话是可以的。

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

  • data 是从请求返回的任何数据。
  • response 是对数据的描述,其中可能包括数据的类型、发送了多少数据、是否有状态代码等等。
  • error 是发生的错误。

现在,巧妙地说,其中一些属性是互斥的,我的意思是,如果发生错误,那么就不会设置data,如果data被发送回去,那么就不会设置error。这种奇怪的状态之所以存在,是因为URLSession API是在Swift出现之前(OC时代)生成的,所以没有更好的方式来表示这种状态。

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

当请求完成时,无论成功与否,都会进入第4步——这是数据任务中的闭包,负责处理数据或错误。在我们的例子中,我们将检查数据是否已设置,以及是否尝试将其解码为响应结构的实例,因为这是iTunes API返回的。实际上,我们并不需要整个Response,只需要其中的结果数组,这样我们的列表将显示它们。

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

iOS需要在主线程上完成所有的用户界面相关工作,主线程是程序启动的地方。因为如果所有与UI相关的工作都发生在主线程上,这将停止两段试图同时操作用户界面的代码,那么它就不会发生冲突。

我们希望将视图的results属性更改为通过iTunes API下载的内容,然后更新我们的用户界面。这在后台线程上可能很有用,因为SwiftUI是超级智能的,但老实说,这不值得冒险——最好是在后台获取数据,在后台从JSON解码,然后在主线程上更新相关属性,以避免任何潜在的问题。

iOS为我们提供了一种向主线程发送工作的非常特殊的方式:DispatchQueue.main.async()。这需要一个要执行的工作闭包,并将其发送到主线程执行。从它的名字可以看出,实际发生的是它被添加到一个队列中——一个等待执行的大工作队列。“async”部分是“asynchronous”的缩写,这意味着我们自己的后台工作不会等待闭包运行;我们只是将它添加到队列中,然后在后台继续工作。

所以,将最后一个代码放在 //step 4注释的位置:

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()使用可选的链接和空合运算符,以确保在存在错误时打印错误,否则给出一个一般错误。

如果你现在运行代码,你应该会看到一个泰勒·斯威夫特歌曲的列表在短暂的停顿之后出现——考虑到最终结果的效果,这真的不是很多代码。

稍后在这个项目中,我们将研究如何自定义URLRequest,以便您可以发送可编码的数据,但现在这已经足够了——请将ContentView.swift重置为其原始状态,以便我们可以开始工作。

译自 Sending and receiving Codable data with URLSession and SwiftUI

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Swift 有效的数独 - LeetCode

    判断一个数独是否有效,根据:Sudoku Puzzles - The Rules。 (数独规则: 每一行不能有重复的数字;每一列不能有重复的数字;将数独框划分...

    韦弦zhy
  • Swift编程小技巧

    Swift中有很多有用的小技巧,用好了能使代码更加安全,简洁,易于理解或效率更加高效,在这记录一些编写swifty code的小技巧。

    韦弦zhy
  • Swift: 有用的标准库全局函数

    全局函数 Global functions :无需特定类型范围就可以从任何地方访问的函数是一个古老的概念,在 C 和 Objective-C 等语言中很流行,但...

    韦弦zhy
  • 市北GMIS | 腾讯优图贾佳亚:人工智能多模态的未来

    之前很多媒体、投资人、各类公司的创始人大佬问我一些问题:人工智能发展到现在,大家从不知道到知道,再到后来知道它有局限性、有发展性,那人工智能现在处于什么阶段呢?...

    机器之心
  • SAP最佳业务实践:FI–现金管理(160)-10银行对账-客户收款-FF67转账

    4.4 银行对账单-客户收款-银行转账 SAP客户收款业务处理: 1、FF67收到银行对账单:收款凭证 借:银行存款 贷:银行结算(中间科目) 2、进一步...

    SAP最佳业务实践
  • 应用分发新三国时代:生态的终极PK

    伴随巨头涌入,国内安卓分发市场渠道日益集中,整体应用分发格局已形成百度领衔、360力保第二、腾讯后劲十足的新三国格局。应用商店之间的PK也已从以往的产品、渠道之...

    罗超频道
  • SAP最佳业务实践:FI–现金管理(160)-20银企对账-供应商付款-转账-FF67手动输入银行对帐单

    4.6.3 FF67手动输入银行对帐单 收到银行对账单,执行对供应商的付款,形成财务凭证如下: 借:银行结算(中间科目) 贷:银行现金 1. 输入一张...

    SAP最佳业务实践
  • 码云周刊:谷歌 Chrome 新功能曝光;GitLab 称有 707 位用户超 5000 个项目丢失数据;下一代PC是这样的

    1、2017 码云招聘-被窝已暖,漂洋过海来睡我 好吧,我承认这是一则寻人启事! 既然来了,何不今日将咱们彼此之间纯洁的革命友谊升华一下?外面冷我已将被窝暖好...

    码云Gitee
  • TW洞见 | 可视化你的足迹

    今日洞见 文章作者来自ThoughtWorks:邱俊涛。 本文所有内容,包括文字、图片和音视频资料,版权均属ThoughtWorks公司所有,任何媒体、网站或个...

    ThoughtWorks
  • 一款java版可接入微信、支付宝的三方支付系统项目

    simple-pay源于本人刚好接手同事交接的支付项目,加上本人对支付的理解,利用业余时间全新开发而成。 简单付(simple-pay)的目的是让开发者对接第三...

    程序源代码

扫码关注云+社区

领取腾讯云代金券