在创建应用程序时,我们通常通过 HTTP 请求的方式从 API 中获取一些数据。在 SwiftUI 中,您可以使用第三方插件(如 Swift-Request 或 Alamofire)来执行此操作,但创建一个自定义的 HTTP 请求函数非常容易。本篇文章我们将自定义一个HTTP请求函数,我们使用一个免费的 REST API。请求调用 /users 端点以获取 10 个用户的列表并将它们显示在我们的视图中。
我们需要创建一个继承 ObservableObject 协议的类。通过使我们的类符合 ObservableObject,类中的更改将自动反映在我们的视图中。让我们创建一个 Network.swift 文件,我们将在其中调用 API。
// Network.swift import SwiftUI class Network: ObservableObject { }
接下来,我们将创建一个User model (用户模型)。通过这样做,我们可以使定义的变量符合用户数据类型。该模型还将允许我们将从 API 获得的 JSON 解码为用户数据类型。此外,我们可以通过简单地编写 user.name 来调用用户名的数据。
首先,我们需要知道我们获得的 JSON 数据的结构。我们可以前往https://jsonplaceholder.typicode.com/users 看看它的结构,如下图所示:
如上图所示,JSON 是一个对象数组。每个对象代表一个用户,它包含许多键值对。
回到我们的项目,我们需要创建一个包含所有这些键值对的结构。为了方便大家练习,小编已经为您创建了 User 模型,如下所示。只需创建一个新的 User.swift 文件,复制以下代码即可:
// User.swift import Foundation struct User: Identifiable, Decodable { var id: Int var name: String var username: String var email: String var address: Address var phone: String var website: String var company: Company struct Address: Decodable { var street: String var suite: String var city: String var zipcode: String var geo: Geo struct Geo: Decodable { var lat: String var lng: String } } struct Company: Decodable { var name: String var catchPhrase: String var bs: String } }
该模型继承 Identifiable 和 Decodable 协议。Identifiable 表示每个项目都有一个唯一的 ID。Decodable 表示意味着它可以被解码——例如,我们可以将一个 JSON 对象转换成这个数据模型。
回到 Network.swift,我们需要在类中创建一个 @Published users 变量。变量的类型将是 Users 数组。我们将使用一个空数组来初始化变量。
// Network.swift class Network: ObservableObject { @Published var users: [User] = [] }
现在,我们需要创建 getUsers 函数来从 API 中获取用户,在 Network 类文件中创建函数。
// Network.swift func getUsers() { guard let url = URL(string: "https://jsonplaceholder.typicode.com/users") else { fatalError("Missing URL") } let urlRequest = URLRequest(url: url) let dataTask = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in if let error = error { print("Request error: ", error) return } guard let response = response as? HTTPURLResponse else { return } if response.statusCode == 200 { guard let data = data else { return } DispatchQueue.main.async { do { let decodedUsers = try JSONDecoder().decode([User].self, from: data) self.users = decodedUsers } catch let error { print("Error decoding: ", error) } } } } dataTask.resume() }
在上面的代码中,首先我们确保有URL地址。使用这个 URL,我们正在创建一个 URLRequest,并将它传递给我们的 dataTask 变量中。
我们正在确保没有错误,并且我们确实得到了接口数据的请求。如果响应为 200 状态(表示接口正确得到了响应),我们会再次检查是否有数据。
然后,我们使用 JSONDecoder 对我们得到的 JSON 格式的数据进行解码,并将数据解码为一个用户数组。解码完成后,我们将其分配给我们在类顶部定义的 users 变量。
最后,我们使用 dataTask.resume() 恢复我们的 dataTask。
现在我们的函数已经创建好了,我们需要在 ProjectNameApp.swift 文件中添加我们的 Network 类作为 EnvironmentObject 。
// ProjectNameApp.swift import SwiftUI @main struct ProjectNameApp: App { var network = Network() var body: some Scene { WindowGroup { ContentView() .environmentObject(network) } } }
在 ContentView 中,将 Network 添加为 EnvironmentObject。
// ContentView.swift struct ContentView: View { @EnvironmentObject var network: Network // More code... }
要预览数据,请记住在 ContentView 的预览中添加 environmentObject,就能在程序运行时,看到数据加载的效果了。
// ContentView.swift struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() .environmentObject(Network()) } }
在 body 方法中,我们添加 ScrollView 和 onAppear 方法,在 onAppear 调用 getUsers() 方法
// ContentView.swift var body: some View { ScrollView { Text("All users") .font(.title).bold() } .onAppear { network.getUsers() } }
然后,只需遍历 network.users。我们将显示每个用户的 ID、姓名、电子邮件和电话。
VStack(alignment: .leading) { ForEach(network.users) { user in HStack(alignment:.top) { Text("\(user.id)") VStack(alignment: .leading) { Text(user.name) .bold() Text(user.email.lowercased()) Text(user.phone) } } .frame(width: 300, alignment: .leading) .padding() .background(Color(#colorLiteral(red: 0.6667672396, green: 0.7527905703, blue: 1, alpha: 0.2662717301))) .cornerRadius(20) } }
注:如果您使用的是预览模式,请记住按预览播放按钮将会触发 API ,请求其中获取数据。如果您使用的是模拟器,结果应该会立即出现。
到这里,我们就实现了一个完整的 HTTP 请求的例子,最后贴出完整的代码,方便大家归纳和使用:
// Network.swift import SwiftUI class Network: ObservableObject { @Published var users: [User] = [] func getUsers() { guard let url = URL(string: "https://jsonplaceholder.typicode.com/users") else { fatalError("Missing URL") } let urlRequest = URLRequest(url: url) let dataTask = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in if let error = error { print("Request error: ", error) return } guard let response = response as? HTTPURLResponse else { return } if response.statusCode == 200 { guard let data = data else { return } DispatchQueue.main.async { do { let decodedUsers = try JSONDecoder().decode([User].self, from: data) self.users = decodedUsers } catch let error { print("Error decoding: ", error) } } } } dataTask.resume() } }
import SwiftUI struct ContentView: View { @EnvironmentObject var network: Network var body: some View { ScrollView { Text("All users") .font(.title) .bold() VStack(alignment: .leading) { ForEach(network.users) { user in HStack(alignment:.top) { Text("\(user.id)") VStack(alignment: .leading) { Text(user.name) .bold() Text(user.email.lowercased()) Text(user.phone) } } .frame(width: 300, alignment: .leading) .padding() .background(Color(#colorLiteral(red: 0.6667672396, green: 0.7527905703, blue: 1, alpha: 0.2662717301))) .cornerRadius(20) } } } .padding(.vertical) .onAppear { network.getUsers() } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() .environmentObject(Network()) } }
最终在模拟器中运行我们的代码,你将会看到如下的效果:
注:本文属于原创文章,版权属于「前端达人」公众号及 SwiftUI.cc 所有,谢绝一切形式的转载
更多精彩内容,请关注「前端达人」