Articles
Swift — 11 Useful Combine Operators You Need to Know

Hello guys, You may already be aware of the Combine framework which provides a declarative Swift API for processing values over time.
Today we will explore the 11 very important Combine operators with examples and graphical representation.
What is an Operator in Combine?
Combine declares publishers to expose values that can change over time, and subscribers to receive those values from the publishers. And the methods that perform an operation on values coming from a publisher are called operators.
Each operator returns a publisher that, receives the values, manipulates them, and then sends them to its subscribers.
Let’s get started!
1. Prepend
This operator prepends or adds values at the beginning of your original publisher.
For example, here we have an array publisher and are performing prepend operation to add value at the beginning of the array.
var subscriptions = Set<AnyCancellable>() let publisher = [5, 6].publisher publisher .prepend(3, 4) .sink(receiveValue: { print($0) } .store(in: &subscriptions) Output: 3, 4, 5, 6
Here, the result must be the same type as items i.e if you have the publisher of Integer
, you can not get String
as a result.
Similarly, we have the Append operator which works the same as Prepend with the difference that it appends values after the original publisher.
2. ReplaceNil
It replaces nil items with the values we specify.
For example, we have one publisher that contains a nil value and we want to replace that nil value with our defined custom value.
[1, 2, nil, 6].publisher .replaceNil(with: 5) .sink(receiveValue: { print($0!) } .store(in: &subscriptions) Output: 1, 2, 5, 6
As a result, we can see that the nil value is replaced with the given number 5.
An important difference between ?? and replaceNil(with:) is that the former can return another optional, while the latter can’t.
3. switchToLatest
It is a more powerful and complex operator as it switches the entire publisher subscriptions stream along with canceling the pending publisher subscription to the latest one as a single stream.
We can only use it on publishers that themselves emit publishers.
Let’s understand it with an example.
let subject1 = PassthroughSubject<Int, Never>() let subject2 = PassthroughSubject<Int, Never>() let subject3 = PassthroughSubject<Int, Never>()let subjects = PassthroughSubject<PassthroughSubject<Int, Never>, Never>()subjects.switchToLatest() .sink(receiveValue: { print($0) }) .store(in: &subscriptions)subjects.send(subject1) subject1.send(1)subjects.send(subject2) subject1.send(2)subject2.send(3) subject2.send(4)subjects.send(subject3)subject2.send(5) subject2.send(6)subject3.send(7) Output: 1 3 4 7
What happens is that each time you send a publisher through publishers, you switch to the new one and cancel the previous subscription.
Consider a real-world example where we have a search text field that is used to detect if an item is available. Once the user inputs something, we want to trigger a request. Our goal is to cancel the previous request if the user has inputted a new value. This can be done with the help of .switchToLatest
.
4. merge(with:)
This operator combines two Publishers
as if we’re receiving values from just one.
Let’s understand it with an example.
let stringSubject1 = PassthroughSubject<String, Never>() let stringSubject2 = PassthroughSubject<String, Never>()stringSubject1 .merge(with: stringSubject2) .sink(receiveValue: { print($0) }) .store(in: &subscriptions)stringSubject1.send("Hello")stringSubject2.send("World") stringSubject2.send("Good")stringSubject1.send("Morning") Output: Hello World Good Morning
As shown here, we can see that all values print as a single stream publisher.
Note: Here, it requires that both publishers must be of the same type.
5. CombineLatest
It combines different publishers of different value types. The emitted output is a tuple with the latest values of all publishers whenever any of them emit a value.
Before .combineLatest emits something, each publisher must emit at least a value.
Let’s understand it with an example.
let subject1 = PassthroughSubject<Int, Never>() let subject2 = PassthroughSubject<String, Never>()subject1 .combineLatest(subject2) .sink(receiveValue: { print("\($0) = \($1)") }) .store(in: &subscriptions)subject1.send(1) subject1.send(2)subject2.send("A") subject2.send("B")subject1.send(3)subject2.send("C") Output: 2 = A 2 = B 3 = B 3 = C
As shown here, the original publisher combines with the latest value of another publisher and makes combinations accordingly.
Consider a real-world example where we have a phone number and country name UITextFields
and a button for allowing us to go ahead with the further process. We want to disable that button until we have enough digits of a phone number and the correct country. We can easily achieve this by using the .combineLatest
operator.
6. zip
It works similar to the .combineLatest
but in this case, it emits a tuple of paired values in the same indexes only after each publisher has emitted a value at the current index.
Let’s understand it with an example.
let subject1 = PassthroughSubject<String, Never>() let subject2 = PassthroughSubject<String, Never>()subject1 .zip(subject2) .sink(receiveValue: { (string1, string2) in print("String1: \(string1), String2: \(string2)") }) .store(in: &subscriptions)subject1.send("Hello") subject1.send("World")subject2.send("Nice")subject1.send("Cool")subject2.send("Rock") subject2.send("Cool") subject2.send("Awesome") Output: String1: Hello, String2: Nice String1: World, String2: Rock String1: Cool, String2: Cool
As shown here, only two zipped
items are emitted from the resulting publisher as subject1
has only 3 items. That’s why the Awesome
value is not printed, because there’s no item to pair it with in subject1
.
7. map
As the name suggests, this operator maps or transforms the elements a publisher emits.
It uses a closure to transform each element it receives from the upstream publisher. We can use map(_:)
to transform from one kind of element to another.
Let’s understand it with an example.
[10, 20, 30] .publisher .map { $0 * 2 } .sink { print($0) } Output: 20 40 60
Here, we can see that publisher values are mapped one by one and are multiplied by two as we specified.
8. collect
It is a powerful operator that allows us to receive all items at once from a publisher. It collects all received elements and emits a single array of the collection when the upstream publisher finishes.
Let’s understand it with an example.
[1, 2, 3, 4].publisher .collect() .sink { (output) in print(output) }.store(in: &subscriptions) Output: [1, 2, 3, 4]
Here, we can see that publisher values are displayed as a single element.
Note: If we don’t use .collect()
operator here then it will simply print as a separate element in a new line instead of a single array.
9. Reduce
This operator iteratively accumulates a new value based on the emissions of the upstream publisher.
It returns the publisher that applies the closure to all received elements and produces an accumulated value when the upstream publisher finishes.
Let’s understand it with an example.
let publisher = ["Hello!", " ", "How ", "Are ", "You?"].publisherpublisher
.reduce("

-
Media4 weeks ago
A Complete Guide on “Login with WhatsApp” in Flutter Apps (w/ backend)
-
Media2 weeks ago
Building Complex Scroll Animations With New iOS 17 API’s
-
Media4 weeks ago
The Ultimate Guide to Android Splash Screen Animations – Splash API
-
Media3 weeks ago
Practical magic with animations in Jetpack Compose