什么是 Combine
Combine 是苹果的响应式编程框架,把"事件流"抽象成可组合的管道:数据从发布者流出,经过一系列操作符变换,到达订阅者。网络、通知、UI 事件都能统一成一条流。
Combine 让异步事件像流水线一样可编排。
和回调、代理相比,Combine 的管线更清晰:map 改数据、filter 过滤、combineLatest 合并多个流,链式写完一遍读下来就是业务逻辑本身。这篇从协议讲起,带你走完一条完整的响应式链路。
Publisher 与 Subscriber
Publisher 定义事件怎么发出,Subscriber 定义怎么接收。中间由订阅关系连接,订阅取消时整个管线停止。Just、Future 是最常用的两个 Publisher。
import Combine
let publisher = Just(42)
let cancel = publisher
.map { $0 * 2 }
.sink { value in
print("收到", value) // 收到 84
}
- Publisher产出值或错误。
- Subscriber接收值并处理。
- Cancelable取消订阅的令牌。
sink 返回 AnyCancellable,要存起来(比如存到属性里),否则订阅会立刻被释放,流就断了。这是 Combine 新手最常见的坑。
常用操作符
操作符是 Combine 的积木:map 变换值、filter 过滤、drop 跳过、merge 合并多流、combineLatest 取多流最新值、flatMap 展开嵌套流。
let numbers = [1, 2, 3, 4, 5].publisher
numbers
.filter { $0 % 2 == 0 }
.map { "偶数: \($0)" }
.sink { print($0) } // 偶数: 2 偶数: 4
操作符链别写太长,超过六个就拆成小函数,不然调试定位麻烦。每个操作符都可能改变输出类型,链的类型跟着变,编译期就能发现大部分错误。
Subjects 与状态管理
Subject 是"既当发布者又能手动发送值"的特殊角色:PassthroughSubject 只转发新值,CurrentValueSubject 保存当前值并转发给新订阅者。
let subject = CurrentValueSubject<Int, Never>(0)
subject.send(1)
subject.send(2)
let sub = subject.sink { print($0) } // 立刻收到 2
CurrentValueSubject 适合做可变状态:订阅即得现值,改了就推送,天然是状态管理的底座。注意别在多个地方 send 同一 subject,状态来源混乱是响应式项目的头号腐化源。
与 SwiftUI 集成
SwiftUI 里 ObservableObject 配合 @Published,属性一变视图自动刷新;Combine 流可以用 assign 直接接到 @Published 上。
class LoginModel: ObservableObject {
@Published var username = ""
@Published var password = ""
@Published var isValid = false
private var bag = Set<AnyCancellable>()
func setup() {
Publishers.CombineLatest($username, $password)
.map { !$0.isEmpty && !$1.isEmpty }
.assign(to: &$isValid)
.store(in: &bag)
}
}
@Published 变量用 $ 前缀拿到 publisher,组合校验后 assign 回状态,UI 自动响应。这套"状态驱动视图"的写法让业务逻辑都在 ViewModel 里,View 只管声明。
实战:登录表单校验
做一个登录页校验:用户名和密码都不为空时按钮可点,密码长度至少六位,防抖后自动显示错误提示。
Publishers.CombineLatest($username, $password)
.map { name, pwd in
name.count >= 3 && pwd.count >= 6
}
.removeDuplicates()
.receive(on: DispatchQueue.main)
.assign(to: &$canSubmit)
.store(in: &bag)
removeDuplicates 避免连续相同值重复触发刷新,receive(on:) 把回调切到主线程再碰 UI。提交按钮绑定 canSubmit,错误文案由密码流单独驱动,一条管线一个职责。
小结
Combine 的核心是"一切皆流":Publisher 产流、操作符编排、Subscriber 消费。CurrentValueSubject 管状态、@Published 接 SwiftUI、assign 自动回写。记住存住 AnyCancellable、主线程碰 UI、状态来源唯一,响应式代码就能又清晰又稳。