Home

Publishers and subscribers

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.

Lifetime

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.