和Publisher相对应的,Subscriber就是观察者模式中的Observer。
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
public protocol Subscriber<Input, Failure> : CustomCombineIdentifierConvertible {
/// The kind of values this subscriber receives.
associatedtype Input
/// The kind of errors this subscriber might receive.
///
/// Use `Never` if this `Subscriber` cannot receive errors.
associatedtype Failure : Error
/// Tells the subscriber that it has successfully subscribed to the publisher and may request items.
///
/// Use the received ``Subscription`` to request items from the publisher.
/// - Parameter subscription: A subscription that represents the connection between publisher and subscriber.
func receive(subscription: Subscription)
/// Tells the subscriber that the publisher has produced an element.
///
/// - Parameter input: The published element.
/// - Returns: A `Subscribers.Demand` instance indicating how many more elements the subscriber expects to receive.
func receive(_ input: Self.Input) -> Subscribers.Demand
/// Tells the subscriber that the publisher has completed publishing, either normally or with an error.
///
/// - Parameter completion: A ``Subscribers/Completion`` case indicating whether publishing completed normally or with an error.
func receive(completion: Subscribers.Completion<Self.Failure>)
}
Publisher在自身状态改变时,调用Subscriber的三个不同方法receive(subscription),receive(_:Input),receive(completion:)来通知Subscriber。
Publisher发出的通知有三种类型:
· Subscription:Subscriber成功订阅的消息,只会发送一次,取消订阅会调用它的cancel方法来释放资源。
· Value(Subscriber的Input,Publisher的Output):真正的数据,可能发送0次或多次。
· Completion:数据流终止的消息,包含两种类型:.finished 和 .failure(Error),最多发送一次,一旦发送完数据流就断开了,有的数据流可能永远没有终止。
大部分场景下,我们需要关心后两种消息:数据流更新和终止。
Combine内置的Subscriber有三种:
· Sink
· Assign
· Subject
Sink
Sink是非常通用的Subscriber,我们可以自由的处理数据流状态。
func subscriberFunc() {
let oncePublisher = Just(100)
let subscriber = Subscribers.Sink<Int, Never> {
print("completed: \($0)")
} receiveValue: { input in
print("receive: \(input)")
}
oncePublisher.receive(subscriber: subscriber)
}
publiser还提供了sink方法来简化整个流程
func sinktestFunc() {
let _ = Just(100).sink {
print("comp: \($0)")
} receiveValue: {
print("receive: \($0)")
}
}
Assign
Assign可以很方便的将接收到的值通过KeyPath设置到指定的Class上(不支持Struct),很适合将已有的程序改造成Reactive。
class People {
let name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
func assignTestFunc() {
let p = People(name: "allien", age: 19)
print("I'm \(p.name), I'm \(p.age) years old.")
let subscriber = Subscribers.Assign(object: p, keyPath: \People.age)
let publisher = PassthroughSubject<Int, Never>()
publisher.subscribe(subscriber)
publisher.send(20)
print("I'm \(p.name), I'm \(p.age) years old.")
publisher.send(22)
print("I'm \(p.name), I'm \(p.age) years old.")
}
经测试后发现,一旦publisher改变了值(调用send方法),People的age也会同步改变。
PassthroughSubject这里是Combine内置的一个Publisher。Subject是中间代理,它有时可能会作为发布者角色出现。