在篇目一中,我们简单讲述了如何使用combine
,但是其真实的使用场景相对灵活多变,因此针对不同使用场景,采取不同的使用方法,就需要我们掌握更多的使用方法,以下是几个combine
的常用方法,非常好用,建议收藏。
组合多个Publisher的结果
Publisher最常用的方式就是获取值后进行通知回调,然后进行计算操作。单个的回调,我们在篇目一中讲解过,这里就不多做介绍了。有些场景下,我们需要等待两个变量都获取值后,再进行操作,比如我个人开发的满满财表,需要等待汇率跟现金都获取到再汇总计算。
针对于这个问题,通常有两种解决办法:
zip
以下是zip的使用方法:
let publisher1 = URLSession.shared.dataTaskPublisher(for: url1)
let publisher2 = URLSession.shared.dataTaskPublisher(for: url2)
Publishers.Zip(publisher1, publisher2)
.sink { completion in
// 处理完成事件
} receiveValue: { data1, data2 in
// 处理两个Publisher的结果
}
CombineLatest
以下是CombineLatest的使用方法:
let publisher1 = URLSession.shared.dataTaskPublisher(for: url1)
let publisher2 = URLSession.shared.dataTaskPublisher(for: url2)
Publishers.CombineLatest(publisher1, publisher2)
.sink { completion in
// 处理完成事件
} receiveValue: { data1, data2 in
// 处理两个Publisher的结果
}
区别
迷糊没??没错,这两个方法在使用上,一摸一样,那么该如何选择呢?
CombineLatest:
- 当任何一个源发布者发出新值时,将获取所有源发布者的最新值并进行组合。
- 生成的新发布者将按照最近接收到的值来更新。
- 发布者之间的值可以不完全对应,只要至少有一个发布者有值,就可以进行组合。
Zip:
- 等待所有源发布者都发出一个新值后,才进行组合。
- 组合的值将以元组的形式呈现,包含所有源发布者的最新值。
- 发布者之间的值必须严格一一对应,否则组合将等待所有发布者都具有对应的值。
也就是说,如果我们其中一个数据源改变就需要进行回调操作,那么我们就应该选择使用CombineLatest,如果我们的使用场景是两个数据源的数据必须全部修改才进行回调,那么我们就应该选择使用Zip。
定时操作和超时处理
有些场景下,数据源的数据并非需要及时回调,这个时候我们就需要采用延时处理,方法如下:
let publisher = ...
let timeoutInterval: TimeInterval = 5.0
publisher
.timeout(timeoutInterval, scheduler: DispatchQueue.main, options: nil) {
// 在超时时执行的闭包
}
.sink { completion in
// 处理完成事件
} receiveValue: { value in
// 处理结果值
}
在上述示例中,使用timeout
操作符设置一个超时时间,如果在指定时间内没有收到新的值,将执行提供的闭包。这可以用于处理网络请求等需要设定超时的场景。
条件切换
import Combine
enum Condition {
case trueCondition
case falseCondition
}
let conditionPublisher = CurrentValueSubject<Condition, Never>(.trueCondition)
let truePublisher = Just("True Publisher")
let falsePublisher = Just("False Publisher")
conditionPublisher
.flatMap { condition -> AnyPublisher<String, Never> in
switch condition {
case .trueCondition:
return truePublisher.eraseToAnyPublisher()
case .falseCondition:
return falsePublisher.eraseToAnyPublisher()
}
}
.sink { value in
print("Received value: (value)")
}
在上述示例中,我们使用CurrentValueSubject
作为条件发布者,并初始化为.trueCondition
。我们定义了两个简单的Just
发布者,分别表示条件为true
和false
时的结果。
然后,我们使用flatMap
操作符将conditionPublisher
的条件值映射到相应的发布者,并使用eraseToAnyPublisher
将结果转换为AnyPublisher
类型。这样,我们就可以将两个不同的发布者进行组合。
最后,我们通过sink
订阅了组合后的发布者,并在接收到值时进行打印。
我们可以根据需要修改条件和结果的类型,并根据具体的业务逻辑和场景定义自己的条件发布者和结果发布者。
自定义操作符
通常情况下,系统提供的combine
操作符就可以满足大部分的操作需求,但是如果在实际开发中遇到了以下场景:
- 组合多个发布者的值:当您需要将多个发布者的值进行合并、组合或转换时,可以使用自定义操作符。例如,将两个发布者的值进行合并、根据条件过滤值、对值进行转换等。
- 封装常用的操作序列:如果您经常使用一系列的操作符来处理发布者的值,您可以将这些操作符封装为一个自定义操作符,以便在代码中重复使用。这样可以提高代码的可读性和可维护性。
- 创建特定功能的操作符:自定义操作符允许您根据自己的需求创建特定功能的操作符。例如,如果您需要执行一些特定的数据处理、过滤、聚合或转换操作,您可以定义一个自定义操作符来满足这些需求。
- 扩展Combine框架的功能:自定义操作符是扩展Combine框架功能的强大工具。您可以使用自定义操作符来填补Combine框架中缺少的某些操作符或功能,以满足特定的需求。
自定义操作符是Combine框架的一个非常强大的功能,它可以帮助您根据自己的需求扩展Combine框架的功能。自定义操作符是通过实现Publisher
协议的扩展来实现的。
下面是一个简单的例子,展示了如何创建一个自定义操作符,将两个发布者的值进行合并:
swiftCopy code
struct MergeOperator<Upstream1: Publisher, Upstream2: Publisher>: Publisher {
typealias Output = Upstream1.Output
typealias Failure = Upstream1.Failure
let upstream1: Upstream1
let upstream2: Upstream2
init(upstream1: Upstream1, upstream2: Upstream2) {
self.upstream1 = upstream1
self.upstream2 = upstream2
}
func receive<S>(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input {
let subscription = MergeOperatorSubscription(subscriber: subscriber)
upstream1.subscribe(subscription)
upstream2.subscribe(subscription)
}
}
class MergeOperatorSubscription<SubscriberType: Subscriber>: Subscription where SubscriberType.Input == Int {
private var subscriber: SubscriberType?
init(subscriber: SubscriberType) {
self.subscriber = subscriber
}
func request(_ demand: Subscribers.Demand) {
// Do nothing
}
func cancel() {
subscriber = nil
}
func receive(_ input: Int) {
_ = subscriber?.receive(input)
}
}
extension Publisher {
func merge<Other: Publisher>(with other: Other) -> MergeOperator<Self, Other> {
return MergeOperator(upstream1: self, upstream2: other)
}
}
let publisher1 = Just(1)
let publisher2 = Just(2)
let mergedPublisher = publisher1.merge(with: publisher2)
let subscription = mergedPublisher
.sink { value in
print("Received merged value: (value)")
}
在上面的例子中,我们首先定义了一个MergeOperator
类型,它实现了Publisher
协议。这个类型包含两个泛型参数Upstream1
和Upstream2
,分别表示要合并的两个发布者类型。MergeOperator
类型还定义了Output
和Failure
类型,它们分别表示合并后的发布者将要发布的值类型和错误类型。
MergeOperator
类型实现了receive(subscriber:)
方法,该方法接收一个订阅者,并将订阅者注册到要合并的两个发布者中。此外,MergeOperator
类型还实现了一个内部的MergeOperatorSubscription
类,它实现了Subscription
协议,它的作用是接收来自要合并的两个发布者的值,并将这些值转发给订阅者。
最后,我们在Publisher
协议的扩展中实现了自定义操作符merge(with:)
,该操作符接收一个要合并的发布者,并将它们传递给MergeOperator
类型的构造函数。