数据持久化与网络请求
概述
在 MVVM 架构中,Model 层负责数据的持久化存储和远程数据获取。本章将介绍如何在 SwiftUI 的 MVVM 架构中实现这两大核心功能。
数据持久化方案
1. UserDefaults
- 适用场景:小型配置数据或用户偏好设置
- 实现方式:
// 存储 UserDefaults.standard.set(value, forKey: "key") // 读取 let value = UserDefaults.standard.string(forKey: "key") - 注意事项:
- 不适合存储大量数据
- 非线程安全,需在主线程操作
2. Core Data
- SwiftUI 集成:
@Environment(\.managedObjectContext) private var viewContext - 最佳实践:
- 使用
NSManagedObject子类作为 Model - 通过
@FetchRequest属性包装器实现数据绑定
- 使用
3. 文件系统存储
- JSON 编码/解码:
struct Todo: Codable { ... } // 编码存储 let data = try JSONEncoder().encode(todos) try data.write(to: fileURL) // 解码读取 let data = try Data(contentsOf: fileURL) let todos = try JSONDecoder().decode([Todo].self, from: data)
网络请求实现
1. URLSession 基础
func fetchTodos() async throws -> [Todo] {
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode([Todo].self, from: data)
}
2. 结合 Combine 框架
class TodoService: ObservableObject {
@Published var todos: [Todo] = []
private var cancellables = Set<AnyCancellable>()
func loadTodos() {
URLSession.shared.dataTaskPublisher(for: url)
.map(\.data)
.decode(type: [Todo].self, decoder: JSONDecoder())
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { _ in },
receiveValue: { [weak self] in self?.todos = $0 })
.store(in: &cancellables)
}
}
3. 错误处理策略
- 网络层错误分类:
enum NetworkError: Error { case invalidURL case requestFailed(Error) case invalidResponse case decodingFailed(Error) } - 重试机制:
.retry(3) .catch { error in Just([]) // 返回空数组作为降级方案 }
MVVM 中的实现模式
1. 服务层抽象
protocol DataServiceProtocol {
func fetchTodos() async throws -> [Todo]
func saveTodos(_ todos: [Todo]) async throws
}
class TodoRepository: DataServiceProtocol {
// 实现具体逻辑
}
2. ViewModel 集成
class TodoListViewModel: ObservableObject {
@Published private(set) var todos: [Todo] = []
private let service: DataServiceProtocol
init(service: DataServiceProtocol = TodoRepository()) {
self.service = service
}
@MainActor
func loadTodos() async {
do {
todos = try await service.fetchTodos()
} catch {
// 处理错误
}
}
}
实战案例:Todo 应用的持久化方案
1. 完整实现流程
- 定义
Todo模型实现Codable协议 - 创建
TodoPersistenceService处理本地存储 - 开发
TodoAPIService处理网络请求 - 在 ViewModel 中协调两种数据源
2. 数据同步策略
func syncData() async {
// 1. 从网络获取最新数据
let remoteTodos = try? await apiService.fetchTodos()
// 2. 与本地数据合并
let mergedTodos = merge(local: localTodos, remote: remoteTodos)
// 3. 保存到本地
try? await persistenceService.save(mergedTodos)
// 4. 更新 ViewModel
await MainActor.run {
self.todos = mergedTodos
}
}
性能优化建议
网络请求优化:
- 使用缓存策略(
URLCache) - 实现分页加载(
offset/limit)
- 使用缓存策略(
持久化优化:
- 批量写入操作
- 后台上下文处理耗时操作
线程安全:
- 确保 Core Data 操作在正确的上下文中执行
- 使用
@MainActor标注 UI 更新方法
