SwiftUI学习100天(Day51 - 项目 10,第三部分)

原创链接:https://www.hackingwithswift.com/100/swiftui
以下内容仅供学习参考:

多年前,一家名为 Sun Microsystems 的公司提出了一个远远超前于时代的口号:“网络就是计算机”。今天,这似乎是显而易见的:无论身在何处,我们都依靠手机、笔记本电脑甚至手表保持联系,因此我们会收到来自世界各地的推送消息、电子邮件、推文等。
想一想:我们今天拥有的 iPhone 得名于 iPod,而 iPod 又得名于 iMac——一款早在 1998 年就推出的产品。Ken Segall 是想出“iMac”这个名字的营销人员,具体来说,“I”代表“internet”,因为在 90 年代,上网并不像今天那么容易。
因此,我们的 iPhone(我们的互联网电话)将网络置于其存在的核心位置也就不足为奇了,而且由于几乎可以保证互联网连接,因此许多应用程序变得更加丰富和有用。今天,你终于要将网络添加到你自己的应用程序中,我希望你对 iOS 为我们提供的如此简单的服务印象深刻!
今天你只有两个主题需要完成,你将在其中创建自定义Codable
实现,然后使用URLSession
发送和接收网络数据。

为 ObservableObject 类编码
我们组织了代码,以便我们有一个Order
对象,可以在我们所有的屏幕之间共享,它的优点是我们可以在这些屏幕之间来回移动而不会丢失数据。然而,这种方法是有代价的:我们必须对类中的属性使用@Published
属性包装器,一旦这样做,我们就失去了对Codable
自动
一致性的支持。
如果你不相信我,只需尝试修改Order
在
Codable
中的定义
,如下所示:
现在构建将失败,因为 Swift 不理解如何编码和解码已发布的属性。这是一个问题,因为我们想将用户的订单提交给互联网服务器,这意味着我们需要它作为 JSON——我们需要Codable
协议才能工作。
这里的修复是手动添加Codable
一致性,这意味着告诉 Swift 应该编码什么,应该如何编码,以及应该如何解码——从 JSON 转换回 Swift 数据。
第一步意味着添加一个符合 CodingKey
的枚举
,列出我们要保存的所有属性。在我们的Order
类中,这几乎是一切——我们唯一不需要的是静态types
属性。
因此,现在将此枚举添加到Order
:
第二步要求我们编写一个encode(to:)
方法,使用我们刚刚创建的编码键枚举创建一个容器,然后写出附加到它们各自键的所有属性。这只是encode(_:forKey:)
反复调用的问题,每次都传入不同的属性和编码密钥。
将此方法添加到Order
现在:
因为该方法标有throws
,所以我们无需担心捕获内部抛出的任何错误——我们可以直接使用try
而不添加catch
,因为知道任何问题都会自动向上传播并在别处处理。
我们的最后一步是实现一个必需的初始化程序,以Order
从一些存档数据中解码一个实例。这几乎与编码相反,甚至受益于相同的throws
功能:
值得在这里补充的是,你可以按照你想要的任何顺序对数据进行编码——你不需要匹配对象中属性声明的顺序。
这使我们的代码完全兼容Codable
:我们有效地绕过了@Published
属性包装器,直接读取和写入值。然而,它并没有使我们的代码编译——事实上,我们现在在 ContentView.swift 中得到了一个完全不同的错误。
现在的问题是我们刚刚为我们的Order
类创建了一个自定义初始化器init(from:)
,而 Swift 希望我们在任何地方都使用它——即使是在我们因为应用程序刚刚启动而只想创建一个新的空订单的地方。
幸运的是,Swift 允许我们为一个类添加多个初始化器,这样我们就可以用多种不同的方式创建它。在这种情况下,这意味着我们需要编写一个新的初始化程序来创建一个没有任何数据的订单——它将完全依赖于我们分配的默认属性值。
因此,现在将这个新的初始化程序添加到Order
:
现在我们的代码返回编译,我们的Codable
一致性已经完成。这意味着我们已经为最后一步做好了准备:Order
通过网络发送和接收对象。



通过互联网发送和接收订单
iOS 带有一些用于处理网络的出色功能,特别是URLSession
类使发送和接收数据变得异常容易。如果我们将它与Codable
将 Swift 对象与 JSON 相互转换,我们可以使用一个新的URLRequest
结构来准确配置数据的发送方式,在大约 20 行代码中完成伟大的事情。
首先,让我们创建一个可以从下订单按钮调用的方法——将其添加到CheckoutView
:
就像我们使用URLSession
下载数据一样
,上传也是异步完成的。
现在将 Place Order 按钮修改为:
这段代码行不通,Swift 很清楚原因:它从不支持并发的函数调用异步函数。它的意思是我们的按钮期望能够立即执行它的动作,并且不明白如何等待某些东西——即使我们写了await placeOrder()
它仍然不会工作,因为按钮不想等待。
之前我提到过它onAppear()
不适用于这些异步函数,我们需要改用task()
修饰符。这不是一个选项,因为我们正在执行一个动作,而不仅仅是附加修饰符,但 Swift 提供了一个替代方案:我们可以凭空创建一个新任务,就像task()
修饰符一样,这将运行任何类型的异步代码我们想要。
事实上,它所要做的就是将我们的await
调用放在一个任务中,如下所示:
现在我们都准备好了——代码将placeOrder()
异步调用就好了。当然,该函数实际上还没有做任何事情,所以现在让我们修复它。
在里面placeOrder()
我们需要做三件事:
将我们当前的
order
对象转换成一些可以发送的 JSON 数据。告诉 Swift 如何通过网络调用发送该数据。
运行该请求并处理响应。
第一个很简单,所以让我们把它写出来。我们已经使Order
类符合Codable
,这意味着我们可以通过在placeOrder()中
添加以下代码来使用JSONEncoder
来存档:
第二步意味着使用一个名为 URLRequest
的新类型
,URL
除了为我们提供了添加额外信息(如请求类型、用户数据等)的选项之外,它类似于一个新类型。
我们需要以一种非常特定的方式附加数据,以便服务器可以正确处理它,这意味着我们需要提供两个额外的数据,而不仅仅是我们的订单:
请求的 HTTP 方法决定了数据应该如何发送。HTTP方法有好几种,但在实践中只有GET(“我要读数据”)和POST(“我要写数据”)用得比较多。我们想在这里写入数据,所以我们将使用 POST。
请求的内容类型决定了发送的数据类型,这会影响服务器处理我们数据的方式。这是在所谓的 MIME 类型中指定的,它最初是为在电子邮件中发送附件而设计的,它有几千个高度特定的选项。
因此,接下来给placeOrder()
的代码
将是创建一个URLRequest
对象,然后将其配置为使用 HTTP POST 请求发送 JSON 数据。然后我们可以使用它来上传我们的数据URLSession
,并处理返回的任何内容。
当然,真正的问题是将我们的请求发送到哪里,我不认为你真的想要设置自己的 Web 服务器来学习本教程。因此,我们将改用一个名为https://reqres.in的非常有用的网站——它可以让我们发送我们想要的任何数据,并会自动将其发回。这是制作网络代码原型的好方法,因为你可以从发送的任何内容中获得真实数据。
将此代码添加到placeOrder()
现在:
第一行包含对URL(string:)
初始化程序的强制解包,这意味着“这将返回一个可选的 URL,但请将其强制为非可选的。” 从字符串创建 URL 可能会失败,因为你插入了一些乱码,但我在这里手动输入了 URL,因此我可以看到它总是正确的——其中没有可能导致问题的字符串插值。
在这一点上,我们都准备好发出我们的网络请求,我们将使用一个名为的新方法URLSession.shared.upload()
和我们刚刚发出的 URL 请求来完成。所以,继续并将其添加到placeOrder
()
:
现在进行重要的工作:当一切正常时,我们需要读取请求的结果。如果出现问题——可能是因为没有互联网连接——那么我们的catch
块将运行,所以我们不必在这里担心。
因为我们使用的是 ReqRes.in,所以我们实际上会取回我们发送的相同订单,这意味着我们可以使用JSONDecoder
来将其从 JSON 转换回对象。
为确认一切正常,我们将显示一个包含订单详细信息的警报,但我们将使用从 ReqRes.in 返回的解码订单。是的,这应该与我们发送的相同,所以如果不是,则意味着我们在编码时犯了错误。
显示警报需要属性来存储消息以及它是否可见,因此现在请将这两个新属性添加到CheckoutView
:
我们还需要附加一个alert()
修饰符来监视该布尔值,并在其为真时立即显示警报。在 CheckoutView
中的导航标题修饰符下方添加此修饰符
:
现在我们可以完成我们的网络代码:我们将解码返回的数据,使用它来设置我们的确认消息属性,然后设置showingConfirmation
为 true 以显示警报。如果解码失败——如果服务器出于某种原因发回了一些不是订单的东西——我们将只打印一条错误消息。
将此最终代码添加到placeOrder()
,替换// handle the result
注释:
有了最终代码,我们的网络代码就完成了,实际上我们的应用程序也完成了。如果你现在尝试运行它,你应该能够选择你想要的确切蛋糕,输入你的送货信息,然后按“下订单”以查看出现的警报!
我们完成了!好吧,我完成了——你还有一些挑战需要完成!


