Publisher用于声明一个能够随时间传递序列值的类型,它是响应式编程模型的 "事件生产者",负责发出数据、完成信号或错误信息。
protocol Publisher {
associatedtype Output // 发布的值类型
associatedtype Failure: Error // 可能抛出的错误类型
func receive<S>(subscriber: S) where S: Subscriber, S.Input == Output, S.Failure == Failure
}
Combine 提供多种开箱即用的 Publisher,覆盖常见异步场景:
表格
| 类型 | 用途 | 特点 |
|---|---|---|
| Just | 发布单个值后立即完成 | 不会产生错误,适用于已知单一结果场景 |
| Future | 包装异步操作,产生单个结果 | 异步执行,完成后发送值或错误 |
| PassthroughSubject | 手动发送值 / 完成 / 错误 | 无初始值,仅传递接收到的事件 |
| CurrentValueSubject | 带初始值的 Subject | 保留最新值,新订阅者会立即收到当前值 |
| Array.publisher | 发布数组元素序列 | 按顺序发送所有元素后完成 |
| URLSession.DataTaskPublisher | 网络请求 | 处理 HTTP 请求,返回数据或错误 |
| Timer.TimerPublisher | 定时发送值 | 按指定时间间隔重复发送值 |
| NotificationCenter.Publisher | 监听通知 | 接收系统通知事件 |
Publisher 的基本使用流程
1. 创建发布者
let stringPublisher = Just("Hello Combine!") // 发布单个字符串
let arrayPublisher = [1, 2, 3, 4].publisher // 发布整数序列
2. 存储订阅引用(防止提前释放)
var cancellables =Set<AnyCancellable>()
3. 订阅发布者
stringPublisher.sink { completion in // 处理完成/错误
switch completion {
case .finished:
print("✅ 完成")
case .failure(let error):
print("❌ 错误: error)")
}
} receiveValue: { value in
// 处理收到的值 print("📥 收到: value)");
}
.store(in: &cancellables) // 存储订阅
// 输出: 📥 收到: Hello Combine! // ✅ 完成
操作符链式处理
[1, 2, 3, 4, 5].publisher
.filter { $0 % 2 == 0 } // 过滤偶数
.map { $0 * 2 } // 乘以2
.sink { value in
print("处理后: value)")
}
.store(in: &cancellables) // 输出: 处理后: 4 // 处理后: 8
Just 可以发布所有 Swift 类型
是一个向订阅者发送单个值,然后立即发送完成事件的发布者
发布单个字符串
1. 创建 Just 发布者(发布单个字符串)
let justPublisher = Just("Hello Combine!")
2. 存储订阅(必须!否则订阅会被立即释放,无法收到值)
var cancellables = Set<AnyCancellable>()
3. 订阅 Publisher
justPublisher.sink { completion in
// 处理完成/错误
} receiveValue: { value in
print("📥 收到值:\(value)")
}
.store(in: &cancellables) // 保存订阅
发布数字
Just(100).sink {
print("整数:\($0)")
}.store(in: &cancellables)
发布数组
Just([1,2,3]).sink {
print("数组:\($0)")
}.store(in: &cancellables)
发布自定义对象
struct User:CustomStringConvertible {
let name: String
var description: String { name }
}
Just(User(name: "张三")).sink {
print("用户:\($0)")
}.store(in: &cancellables)
Future 是一个异步执行闭包,最终通过 Promise 发布单个成功值或单个错误,然后立即完成的 Publisher
用途:处理网络请求、延时任务、文件读写等异步单结果场景
1. 创建 Future:包装异步延时任务
let futurePublisher = Future<Int, Error> { promise in
// 异步逻辑:延时 1 秒后返回结果
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
let number = Int.random(in: 1...100) // 模拟成功/失败(随机)
if number > 10 {
// ✅ 成功:发送单个值 promise(.success(number))
} else {
// ❌ 失败:发送错误
promise(.failure(NSError(domain: "数值太小", code: 0)))
}
}
}
2. 存储订阅(必须)
var cancellables =Set<AnyCancellable>()
3.订阅 Future
futurePublisher.sink { completion in
switch completion {
case .finished:
print("✅ 发布完成")
case .failure(let error):
print("❌ 失败:\(error.localizedDescription)")
}
} receiveValue: { value in
print("📥 异步收到值:\(value)")
}.store(in:&cancellables)
PassthroughSubject 是一种既可以当发布者、又可以当订阅者的类型,手动发送事件,不会保存任何历史值,新订阅者只能收到订阅之后发出的事件。
UI交互、组件通信的首选
1.创建 PassthroughSubject // 发送 String 类型值,永不失败(Failure = Never)
let passthroughSubject =PassthroughSubject<String, Never>()
2.存储订阅(必须!)
var cancellables = Set<AnyCancellable>()
3.订Subject
passthroughSubject.sink { completion in
print("✅ 订阅结束:\(completion)")
} receiveValue: { value in
print("📥 收到实时值:\(value)")
}.store(in: &cancellables)
4.手动发送事件(核心!)
passthroughSubject.send("第一条消息")
passthroughSubject.send("第二条消息")
passthroughSubject.send("第三条消息")
5.手动发送完成信号(结束数据流)
passthroughSubject.send(completion: .finished)
6.完成后再发值:无效!
passthroughSubject.send("这条消息收不到")
CurrentValueSubject 是一种包装单个值的 Subject,会永久保存最新的一个值,新订阅者会立即收到当前缓存值,之后再接收后续更新的事件
用途:状态管理、UI 绑定
1. 创建 CurrentValueSubject(必须传初始值:0)
let countSubject = CurrentValueSubject<Int, Never>(0)
// 存储订阅
var cancellables = Set<AnyCancellable>()
2. 第一次订阅
countSubject .sink { value in
print("【订阅1】收到值:\(value)")
}.store(in: &cancellables)
3. 更新值(两种方式效果完全一致)
countSubject.send(1) // 方式1:send() 发送
countSubject.value = 2 // 方式2:直接修改 .value 属性(最常用)
4. 【重点】第二次订阅:立即收到当前最新值(2)
countSubject.sink { value in
print("【订阅2】收到值:\(value)")
}.store(in: &cancellables)
// 继续更新
countSubject.send(3)
5.随时读取当前缓存值
print("当前最新值:\(countSubject.value)")
6.结束数据流 countSubject.send(completion: .finished)
错误处理
使用`.catch { ... }`处理错误并返回新的 Publisher
使用`.replaceError(with:)`用默认值替换错误
使用`.assertNoFailure()`强制无错误 (开发调试用)