第二部分:MVVM 在 SwiftUI 中的实现
第5章:开发 ViewModel 层
数据绑定与 Combine 框架
核心概念
Combine 框架简介
- Apple 推出的响应式编程框架,用于处理异步事件和数据流
- 基于 Publisher-Subscriber 模式,实现数据变化的自动传播
- 与 SwiftUI 深度集成,是 MVVM 中数据绑定的理想解决方案
关键组件
@Published属性包装器:标记需要观察的属性ObservableObject协议:使对象具备发布变化的能力PassthroughSubject/CurrentValueSubject:手动控制的事件流
实现数据绑定
class TodoViewModel: ObservableObject {
@Published var items: [TodoItem] = []
private var cancellables = Set<AnyCancellable>()
func fetchData() {
NetworkService.fetchTodos()
.sink(receiveCompletion: { _ in },
receiveValue: { [weak self] items in
self?.items = items
})
.store(in: &cancellables)
}
}
视图层绑定示例
struct TodoListView: View {
@ObservedObject var viewModel: TodoViewModel
var body: some View {
List(viewModel.items) { item in
Text(item.title)
}
.onAppear {
viewModel.fetchData()
}
}
}
高级技巧
双向绑定
- 使用
Binding包装器实现视图与 ViewModel 的双向交互
TextField("Title", text: $viewModel.newItemTitle)- 使用
数据流组合
- 使用 Combine 操作符(
map/filter/combineLatest)处理复杂逻辑
$searchText .combineLatest($items) .map { text, items in text.isEmpty ? items : items.filter { $0.title.contains(text) } }- 使用 Combine 操作符(
错误处理
- 通过
catch和replaceError处理网络请求异常
- 通过
性能优化
- 使用
debounce处理高频事件(如搜索输入) - 避免在主线程执行耗时操作
- 及时释放
AnyCancellable防止内存泄漏
常见问题解决方案
绑定失效
- 确保遵守
ObservableObject协议 - 检查
@Published属性是否被正确标记
- 确保遵守
内存泄漏
- 使用
[weak self]捕获列表 - 在适当时机调用
cancel()
- 使用
线程冲突
- 使用
receive(on:)指定 UI 更新线程
.receive(on: DispatchQueue.main)- 使用
最佳实践:将 Combine 逻辑封装到 ViewModel 的扩展中,保持主要业务逻辑的清晰性。
