学习目标
- 掌握 SwiftUI 中的数据处理基本方法
- 了解如何进行网络请求
- 学习如何处理网络请求的加载状态和错误
- 掌握数据过滤和排序的方法
- 了解如何使用 JSONDecoder 解析 JSON 数据
核心概念
数据模型
在 SwiftUI 中,数据模型通常使用结构体来定义,并且需要符合 Identifiable 协议以便在列表中使用。
示例代码:
struct Post: Identifiable, Decodable {
let id: Int
let title: String
let body: String
let userId: Int
}
本地数据处理
本地数据处理包括数据的添加、删除、修改和查询等操作。
示例代码:
@State private var localData: [String] = ["本地数据1", "本地数据2", "本地数据3"]
@State private var newData = ""
HStack {
TextField("输入新数据", text: $newData)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
Button("添加") {
if !newData.isEmpty {
localData.append(newData)
newData = ""
}
}
}
List(localData, id: \.self) { item in
Text(item)
}
网络请求
在 SwiftUI 中,网络请求通常使用 URLSession 来实现,并且需要在后台线程中执行,然后在主线程中更新 UI。
示例代码:
@State private var posts: [Post] = []
@State private var isLoading = false
@State private var errorMessage: String? = nil
func fetchPosts() {
isLoading = true
errorMessage = nil
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else {
self.isLoading = false
self.errorMessage = "无效的URL"
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
DispatchQueue.main.async {
self.isLoading = false
if let error = error {
self.errorMessage = error.localizedDescription
return
}
guard let data = data else {
self.errorMessage = "无数据返回"
return
}
do {
let decodedPosts = try JSONDecoder().decode([Post].self, from: data)
self.posts = decodedPosts
} catch {
self.errorMessage = "解析数据失败"
}
}
}.resume()
}
数据状态管理
在网络请求过程中,需要管理不同的状态:加载中、加载成功和加载失败。
示例代码:
if isLoading {
ProgressView("加载中...")
.padding()
} else if let errorMessage = errorMessage {
Text("错误: \(errorMessage)")
.foregroundColor(.red)
.padding()
} else {
List(posts) { post in
VStack(alignment: .leading) {
Text(post.title)
.font(.headline)
Text(post.body)
.font(.body)
.foregroundColor(.gray)
}
}
}
数据过滤
使用 filter 方法可以对数据进行过滤,只显示符合条件的数据。
示例代码:
List(localData.filter { $0.contains("1") }, id: \.self) {
Text($0)
}
数据排序
使用 sorted 方法可以对数据进行排序,按照指定的规则排列数据。
示例代码:
List(localData.sorted(), id: \.self) {
Text($0)
}
实践示例:完整数据处理与网络请求演示
以下是一个完整的数据处理与网络请求演示示例:
import SwiftUI
// 数据模型
struct Post: Identifiable, Decodable {
let id: Int
let title: String
let body: String
let userId: Int
}
struct DataProcessingAndNetworkingDemo: View {
// 状态管理
@State private var posts: [Post] = []
@State private var isLoading = false
@State private var errorMessage: String? = nil
@State private var localData: [String] = ["本地数据1", "本地数据2", "本地数据3"]
@State private var newData = ""
@State private var filterKeyword = ""
@State private var sortAscending = true
var body: some View {
ScrollView {
VStack(spacing: 25) {
// 标题
Text("数据处理与网络请求")
.font(.largeTitle)
.fontWeight(.bold)
.foregroundColor(.blue)
// 1. 本地数据处理
VStack(alignment: .leading, spacing: 12) {
Text("1. 本地数据处理")
.font(.headline)
HStack {
TextField("输入新数据", text: $newData)
.textFieldStyle(.roundedBorder)
Button("添加") {
if !newData.isEmpty {
localData.append(newData)
newData = ""
}
}
.buttonStyle(.borderedProminent)
}
List(localData, id: \.self) { item in
Text(item)
}
.frame(height: 150)
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(10)
// 2. 网络请求
VStack(alignment: .leading, spacing: 12) {
Text("2. 网络请求")
.font(.headline)
Button("获取网络数据") {
fetchPosts()
}
.buttonStyle(.borderedProminent)
if isLoading {
ProgressView("加载中...")
.padding()
} else if let errorMessage = errorMessage {
Text("错误: \(errorMessage)")
.foregroundColor(.red)
.padding()
} else if !posts.isEmpty {
List(posts) { post in
VStack(alignment: .leading) {
Text(post.title)
.font(.headline)
Text(post.body)
.font(.body)
.foregroundColor(.gray)
}
}
.frame(height: 250)
}
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(10)
// 3. 数据过滤
VStack(alignment: .leading, spacing: 12) {
Text("3. 数据过滤")
.font(.headline)
TextField("输入过滤关键词", text: $filterKeyword)
.textFieldStyle(.roundedBorder)
List(localData.filter {
filterKeyword.isEmpty ? true : $0.contains(filterKeyword)
}, id: \.self) { item in
Text(item)
}
.frame(height: 120)
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(10)
// 4. 数据排序
VStack(alignment: .leading, spacing: 12) {
Text("4. 数据排序")
.font(.headline)
Toggle("升序排列", isOn: $sortAscending)
List(sortAscending ? localData.sorted() : localData.sorted(by: >), id: \.self) { item in
Text(item)
}
.frame(height: 120)
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(10)
}
.padding()
}
}
// 网络请求方法
func fetchPosts() {
isLoading = true
errorMessage = nil
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else {
self.isLoading = false
self.errorMessage = "无效的URL"
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
DispatchQueue.main.async {
self.isLoading = false
if let error = error {
self.errorMessage = error.localizedDescription
return
}
guard let data = data else {
self.errorMessage = "无数据返回"
return
}
do {
let decodedPosts = try JSONDecoder().decode([Post].self, from: data)
self.posts = decodedPosts
} catch {
self.errorMessage = "解析数据失败"
}
}
}.resume()
}
}
#Preview {
DataProcessingAndNetworkingDemo()
}
常见问题与解决方案
1. 网络请求在主线程执行
问题:网络请求在主线程执行,导致 UI 卡顿。
解决方案:使用 DispatchQueue.global().async 将网络请求放在后台线程执行,然后在主线程中更新 UI。实际上 URLSession.dataTask 的回调默认就在后台线程,只需确保 UI 更新在 DispatchQueue.main.async 中。
URLSession.shared.dataTask(with: url) { data, response, error in
DispatchQueue.main.async {
// 更新 UI
}
}.resume()
2. 数据解析失败
问题:JSON 数据解析失败。
解决方案:
- 确保数据模型与 JSON 数据结构完全匹配(字段名、类型)
- 使用
CodingKeys处理字段名不一致的情况 - 使用
try?或do-catch捕获错误
struct Post: Decodable {
let id: Int
let title: String
enum CodingKeys: String, CodingKey {
case id
case title = "post_title" // 如果 JSON 字段名不同
}
}
3. 加载状态未正确显示
问题:网络请求过程中没有显示加载状态。
解决方案:使用 @State 变量管理加载状态,并在请求开始前设置为 true,完成后设置为 false。
4. 错误处理不完善
问题:网络请求失败时没有显示错误信息。
解决方案:捕获并处理网络请求中的错误,将错误信息显示给用户。
if let errorMessage = errorMessage {
Text("错误: \(errorMessage)")
.foregroundColor(.red)
}
总结
本章介绍了 SwiftUI 中的数据处理与网络请求,包括:
- 数据模型的定义:使用
Identifiable和Decodable协议 - 本地数据处理:增删改查、列表展示
- 网络请求的实现:使用
URLSession和异步回调 - 数据状态管理:加载中、成功、失败三种状态
- 数据过滤:使用
filter方法按条件筛选 - 数据排序:使用
sorted方法自定义排序规则
通过这些技术,可以实现数据的获取、处理和展示,为应用提供丰富的数据源。在实际开发中,数据处理与网络请求是应用的核心功能之一,掌握这些技术对于开发高质量的 SwiftUI 应用至关重要。
参考资料
- SwiftUI 官方文档 - State
- Apple Developer Documentation: URLSession
- Apple Developer Documentation: JSONDecoder
- SwiftUI Networking Tutorial
- Hacking with Swift: SwiftUI networking
本内容为《SwiftUI 进阶》第五章,欢迎关注后续更新。