开发iOS应用时,界面和数据的同步一直是个让人头疼的问题。以前用UIKit,得手动写很多代理、通知或者KVO来监听变化。现在有了SwiftUI和Combine,事情变得简单多了。
为什么SwiftUI和Combine是天作之合
SwiftUI负责界面声明,你只需要描述“界面应该长什么样”,系统自动处理更新。Combine则是响应式编程框架,用来处理随时间变化的数据流。两者结合,数据一变,界面自动刷新,不用再手动调用reloadData或者更新label.text。
比如做个天气App,温度数据从网络返回后,通过Combine推送到视图层,SwiftUI立刻重新渲染。整个过程像流水线一样顺畅,中间几乎不需要你插手。
一个简单的例子
假设我们有个按钮,点击后发起网络请求获取用户信息,然后显示在界面上。用SwiftUI写界面,用Combine管理请求流程:
import SwiftUI
import Combine
class UserViewModel: ObservableObject {
@Published var userName = "加载中..."
private var cancellables = Set<AnyCancellable>()
func fetchUser() {
URLSession.shared.dataTaskPublisher(for: URL(string: "https://api.example.com/user")!)
.map { data, _ in String(data: data, encoding: .utf8) ?? "未知" }
.replaceError(with: "获取失败")
.receive(on: DispatchQueue.main)
.assign(to: \ .userName, on: self)
.store(in: &cancellables)
}
}
对应的SwiftUI视图:
struct UserView: View {
@StateObject private var viewModel = UserViewModel()
var body: some View {
VStack {
Text(viewModel.userName)
.font(.headline)
Button("刷新用户") {
viewModel.fetchUser()
}
}
}
}
@Published标记的属性会自动发出值变化事件,Combine接住这些事件,经过处理后更新到界面。DispatchQueue.main确保更新发生在主线程,避免卡顿或崩溃。
实际场景中的好处
你在做购物车功能,商品数量变化时,总价要实时计算。用Combine的combineLatest可以同时监听多个商品的数量流,一旦任何一个变动,就重新计算总金额。
这种模式比到处写didSet回调清晰多了。逻辑集中在一处,改起来也方便。而且代码读起来像是在描述业务流程,而不是一堆零散的操作。
再比如表单验证,输入邮箱后要实时检查格式是否正确,并决定提交按钮是否可用。用Combine链式调用filter、debounce、map,几行代码就能搞定延迟验证和状态同步。
注意事项
别忘了把订阅存进Set<AnyCancellable>,否则可能内存泄漏。Combine的订阅不会自动释放,必须显式管理生命周期。@StateObject能帮我们在视图销毁时清理资源,但前提是你要把订阅正确存进去。
另外,不是所有场景都非得用Combine。简单的状态更新,用@State和binding完全够用。Combine适合处理异步、多步骤、带错误处理的数据流。用对地方才高效。