The contract underneath every operator
A Publisher promises to deliver values of one type and to fail with one error type. A Subscriber
promises to accept them and to say how many it can take.
protocol Publisher<Output, Failure> {
associatedtype Output
associatedtype Failure: Error
func receive<S: Subscriber>(subscriber: S)
where S.Input == Output, S.Failure == Failure
}
The part people miss is demand. A subscriber does not passively receive; it requests a number of
values, and the publisher may not send more than that. This is Combine’s backpressure, and it is why
sink — which requests .unlimited — is the wrong tool the moment the producer is faster than the
consumer.
A subscription lives exactly as long as the AnyCancellable you hold:
final class SearchModel {
private var cancellables: Set<AnyCancellable> = []
func start() {
publisher.sink { … }.store(in: &cancellables)
}
}
Drop the reference and the stream stops mid-flight — a silent bug, because nothing errors and nothing logs.
This is a template chapter. The full text is still being written.