译自 www.hackingwithswift.com/books/ios-s…
更多内容,欢迎关注公众号 「Swift花园」
喜欢文章?不如来个 🔺💛➕三连?关注专栏,关注我 🚀🚀🚀
在网络上发送和接收订单
对于处理网络请求,iOS 内建了很棒的功能,尤其是URLSession
类使得发送和接收数据变得相当简单。结合Codable
,我们在 Swift 对象和 JSON 之间互相转换,加上URLRequest
,使得我们能够准确地配置要发送的数据,我们可以用少于 20 行的代码完成很酷的事情。
首先,我们创建一个方法,在 Place Order 按钮被点击时调用 —— 把下面这个方法添加到CheckoutView
:
func placeOrder() {
}
然后把 Place Order 按钮改成这样:
Button("Place Order") {
self.placeOrder()
}
.padding()
在placeOrder()
方法里,我们需要做三件事:
- 把当前的
order
对象转换成 JSON 数据,以便发送 - 准确一个
URLRequest
,用来编码后的 JSON 数据 - 运行请求,处理响应
第一步很直接,因为我们已经让Order
类遵循Codable
协议,所以我们可以用JSONEncoder
来归档它,把下面的代码添加到placeOrder()
:
guard let encoded = try? JSONEncoder().encode(order) else {
print("Failed to encode order")
return
}
第二步 —— 准备URLRequest
以发送我们的数据 —— 需要更多的思考。想想看,我们需要将数据一种特定的方式发给服务器,以便它能正确处理,这意味着我们需要在订单之外提供两个额外的字段:
- 决定数据如何发送的 HTTP 方法。有几种 HTTP 方法,实践上只有 GET (“我想要读取数据”) 和 POST (“我想要写入数据”) 最为常用。这里我们是想写数据,因为我们用 POST 方法。
- 请求的内容类型决定了被发送的数据的种类,这会影响服务端对待数据的方式。这一点是 MIME 类型来确定的,最初被设计用于发送电子邮件的附件,它有非常多的选项。
因此,接下来placeOrder()
的代码是创建一个URLRequest
,并且配置它以 HTTP POST 方法来发送 JSON 数据,并且附上我们的数据。
当然,实际的问题是,我们要往哪里发送我们的请求,我想我们不会为了这个教程而去搭建你自己的 web 服务器。因此,我们会用到一个很有用的网站,叫做reqres.in,它允许我们发送任意的数据,然后自动发送回来。对于网络功能的原型,这种方法很棒,因为你会基于你发送的任何数据,获得实际的数据反馈。
把下面的代码添加到placeOrder()
:
let url = URL(string: "https://reqres.in/api/cupcakes")!
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = encoded
注意,这里我给URL(string:)
构造器添加了一个强制解包。基于字符串创建 URL 是有可能失败的,因为你可能传入了一个不靠谱的 URL 字符串,但这里我手打的 URL 是可以被确信是正确的 —— 这里头没有字符串插值可能导致问题。
到这里,网络请求已经设置好,我们要把它用于URLSession.shared.dataTask()
和我们刚刚创建的 URL 请求。记住,如果你忘记在dataTask
上调用resume()
,它不会启动。这也正是为什么我每次写 task 时,总是在填充方法体之前提前写好resume
调用的原因。
接下来,把下面的方法添加到placeOrder()
:
URLSession.shared.dataTask(with: request) { data, response, error in
// handle the result here.
}.resume()
然后是最重要的部分:我们需要读取请求的结果。如果出现错误 —— 比如没有网络连接 —— 我们打印消息并且返回。
把下面这个方法添加到placeOrder()
:
guard let data = data else {
print("No data in response: \(error?.localizedDescription ?? "Unknown error").")
return
}
如果上面的 guard 语句通过,说明我们从服务器拿到了某条数据。因为我们用的是 ReqRes.in,所以实际上拿到正是我们发出去的同一条订单数据。因此,我们可以用JSONDecoder
把 JSON 解码回对象。
为了确认一切工作正常,我们需要显示一个包含订单细节的 alert。这个订单应该同我们发出去的完全一致,否则说明我们的代码中有错误。
显示一个 alert 需要用到存储消息的属性,以及是否显示的标记,把下面两个属性添加到CheckoutView
:
@State private var confirmationMessage = ""
@State private var showingConfirmation = false
把下面这个 modifier 添加到CheckoutView
的GeometryReader
:
.alert(isPresented: $showingConfirmation) {
Alert(title: Text("Thank you!"), message: Text(confirmationMessage), dismissButton: .default(Text("OK")))
}
接下来完成网络代码:我们需要解码返回的数据,并用解码后的数据设置消息,然后把showingConfirmation
设置为true
,以便 alert 显示。如果解码失败 —— 比如因为某种原因服务器返回的东西并不是订单 —— 那我们可以打印一条错误消息。
把最后的代码添加到placeOrder()
,放在dataTask(with:)
的完成闭包那里:
if let decodedOrder = try? JSONDecoder().decode(Order.self, from: data) {
self.confirmationMessage = "Your order for \(decodedOrder.quantity)x \(Order.types[decodedOrder.type].lowercased()) cupcakes is on its way!"
self.showingConfirmation = true
} else {
print("Invalid response from server")
}
完成了网络代码,整个应用实际上也完成了。尝试运行代码,你可以选择蛋糕,输入配送信息,然后点击 Place Order ,查看 alert 显示。
完工。
我的公众号 这里有Swift及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~