What is Combine?
Combine Framework provides an API for processing async events over time such as user-input, network response, and other dynamic data.
What is the purpose of Combine?
The purpose of Combine is to simplify the management of async events and data streams.
Publishers
Publisher
declares that a type can transit a sequence of values over time. A publisher delivers elements to one or more Subscriber
instances.
class PostService {
func fetchPosts() -> AnyPublisher<[Post], Error> {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else {
fatalError("Invalid URL")
}
return URLSession.shared.dataTaskPublisher(for: url)
.map(\.data)
.decode(type: [Post].self, decoder: JSONDecoder())
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
}
Subscribers
Subscriber
is a protocol that declares a type that can receive input from a publisher.
A Subscriber instance receives a stream of elements from a Publisher.
private var cancellable: AnyCancellable?
let service = PostService()
cancellable = service.fetchPosts()
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
break
case .failure(let error):
print(error.localizedDescription)
}
}, receiveValue: { posts in
print("Received posts count:", posts.count)
})
AnyCancellable
When you call a method like sink
or assign
on a publisher, it returns a type that conforms to the Cancellable
protocol. Storing this return value in an instance of AnyCancellable
keeps the subscription active. When the AnyCancellable instance is deallocated, its deinit method automatically cancels the subscription.
var cancellables = Set<AnyCancellable>()
let publisher = Just("Hello, Combine!")
publisher
.sink { completion in
print("Completion: \(completion)")
} receiveValue: { value in
print("Received value: \(value)")
}
.store(in: &cancellables)
Operators
Transforming Operators
map
: Transforms each value received from a publisher by applying a function.flatMap
: Transforms each value received into a new publisher, then flattens the result into a single publisher stream.scan
: Applies a closure over the previous result and the current value to produce a new value, useful for accumulating values.
Filtering Operators
filter
: Emits only those values from a publisher that satisfy a given predicate.removeDuplicates
: Suppresses duplicate consecutive values published by a publisher.first/last
: Emits only the first or last value from a publisher that satisfies a predicate condition.
Combining Operators
combineLatest
: Combines the latest value from two or more publishers and emits a combined value each time any of the publishers emit a value.merge
: Combines multiple publishers of the same type into a single publisher stream, emitting values as they arrive.zip
: Combines values from multiple publishers into tuples, emitting a tuple only when each of the publishers has emitted a new value.
Error Handling Operators
catch
: Handles errors from a publisher by replacing the failed publisher with another publisher or a value.retry
: Attempts to recreate a subscription to a failed publisher for a specified number of times.
Utility Operators
delay
: Delays the emission of items from the publisher for a specified interval.subscribe(on:)/receive(on:)
: Specifies the dispatch queue for performing subscription work or receiving values.print
: Prints log messages for all publisher events to the console, useful for debugging.
Timing Operators
debounce
: Emits a value from a publisher only after a specified time interval has passed without receiving another value.throttle
: Emits either the first or last value received in a specified time window.
Collecting Operators
collect
: Collects received values and emits an array of those values either when the publisher completes or when a buffer size is reached.
When to use Combine?
I found great advice from Apple when it comes to Combine:
“A Combine publisher fills a role similar to, but distinct from, the
AsyncSequence
in the Swift standard library. APublisher
and anAsyncSequence
both produce elements over time. However, the pull model in Combine uses aSubscriber
to request elements from a publisher, while Swift concurrency uses the for-await-in syntax to iterate over elements published by anAsyncSequence
. Both APIs offer methods to modify the sequence by mapping or filtering elements, while only Combine provides time-based operations likedebounce(for:scheduler:options:)
andthrottle(for:scheduler:latest:)
, and combining operations likemerge(with:)
andcombineLatest(_:_:)
. To bridge the two approaches, the property values exposes a publisher’s elements as anAsyncSequence
, allowing you to iterate over them withfor-await-in
rather than attaching aSubscriber
.”
Thank you for reading!