What is Combine?
The Combine framework provides a declarative Swift API for processing values over time. These values can represent many kinds of asynchronous events. Combine declares publishers to expose values that can change over time, and subscribers to receive those values from the publishers.
Key concept
Publisher
Publisher is the center of the entire process, it delivers elements to one or more Subscriber instances
public protocol Publisher {
associatedtype Output
associatedtype Failure : Error
func receive<S>(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input
}
All the Publisher inherit this protocol. Publisher protocol has 2 types you have to provide:
Output: Type of data emitted
Failure: Type of data for error
1 publisher will emit one of the following three types of values:
value: is what we need, the data
error: subscribers will receive the error
complete: is the end of the life cycle of a publisher
Subscriber
Subscribers will receive values from Publisher, all subscribers must inherit the Subscriber protocol.
public protocol Subscriber {
associatedtype Input
associatedtype Failure : Error
func receive(subscription: Subscription)
func receive(_ input: Self.Input) -> Subscribers.Demand
func receive(completion: Subscribers.Completion<Self.Failure>)
}
Input: Type of data provided
Failure: Type of data for error
3 important methods:
receive(subscription:)
when receiving a subscription from Publisherreceive(input:)
when receiving value from Publisher and we will adjust the request for further data through Demand.receive( completion:)
when receiving completion from Publisher.
There are some ways to init a subscriber such as sink
, assign
, …
Life Cycle
Publisher Creation:
The lifecycle begins when you create a publisher. A publisher emits values over time, representing a data stream.
Publishers can be created from various sources (e.g., arrays, network requests, user interactions).
Subscriber Subscription:
When you subscribe to a publisher, a subscription is established.
The subscriber (consumer) receives values emitted by the publisher.
The subscription manages the connection between the publisher and subscriber.
Subscription Request:
The subscriber can request a specific number of values using the
request(_:)
method on the subscription.The demand can be:
.none
: No demand for values..unlimited
: Wants all values..max(Int)
: Limits the number of items received.
Value Emission:
As the publisher produces values (e.g., from an array or network response), it sends them to the subscriber.
The subscriber’s
receive(_:)
method is called for each emitted value.
Completion or Error Handling:
Publishers can complete normally or encounter errors.
When the publisher completes, the subscriber’s
receive(completion:)
the method is called with a.finished
completion.If an error occurs, the subscriber receives a
.failure(Error)
completion.
Subscriber Cancellation:
Subscribers can cancel their subscription at any time using the
cancel()
method.Cancellation releases resources and stops further value emissions.
Example Usage:
import Combine
let numbers = [1, 2, 3, 4, 5]
let publisher = numbers.publisher
let subscriber = Subscribers.Sink<Int, Never> { value in
print("Received value:", value)
}
let subscription = publisher.subscribe(subscriber)
subscription.request(.max(3)) // Request only 3 values
// Output:
Received value: 1
Received value: 2
Received value: 3
Operators
In-between Publisher
and Subscriber
, they transform/filter the published value into something desired by the subscriber
Operators forward errors, only change values. They also manage scheduling/time, thread/queue movement, and more
Thanks for Reading! ✌️
If you have any questions or corrections, please leave a comment below or contact me via my LinkedIn account Pham Trung Huy.
Happy coding 🍻