This is Understanding Combine, written by Matt Neuburg. Corrections and suggestions are greatly appreciated (you can comment here). So are donations; please consider keeping me going by funding this work at http://www.paypal.me/mattneub. Or buy my books: the current (and final) editions are iOS 15 Programming Fundamentals with Swift and Programming iOS 14. Thank you!
Introducing Operators
You now know that the Combine framework unifies various forms of asynchronous signal into a single uniform API using a publish-and-subscribe architecture. Nonetheless, if that were all the Combine framework did, it still might not be sufficiently interesting and powerful for you to be motivated to learn about it.
Well, I’ve been keeping something up my sleeve. I’ve left out the most important part of the story — operators. This is where things start to get really impressive. To put it another way: the fun is about to begin!
An operator is an object that acts both as a publisher and as a subscriber. This means that it can sit within a chain of objects running between our original publisher (such as NotificationCenter.default.publisher
) and our original subscriber (such as sink
). We end up with a graph of objects in a structure that I can show schematically like this:
publisher [upstream]
⬇️ operator
⬇️ operator
⬇️ ...
subscriber [downstream]
In the schema above, I’ve called the publisher end of the chain the “upstream” direction and the subscriber end of the chain the “downstream” direction. The arrows on the left indicate the downstream flow of signals. The signal produced by the publisher passes from the upstream object, through each operator, in turn, and on out to the downstream object, on its way to the subscriber.
The way this works is that each operator inside the chain effectively subscribes to whatever is immediately upstream of it, and is in turn subscribed to by whatever is immediately downstream of it, like this:
publisher [upstream] ⬇️ publish
⬇️ operator ⬆️ subscribe ⬇️ publish
⬇️ operator ⬆️ subscribe ⬇️ publish
⬇️ ... ⬆️ subscribe ⬇️ publish
subscriber [downstream] ⬆️ subscribe
So each operator in the chain receives a signal from upstream and can respond by sending a signal downstream.
But why would you want to do that? Well, I didn’t say that the signal produced downstream by an operator has to be identically the same signal that the operator received from upstream. An operator doesn’t have to echo the signal it receives. On the contrary, each operator in the chain, in turn, has a chance to transform the signal as it passes down the chain. The signal is a value of some sort. An operator can do things with that value. For instance:
It can change that value.
It can change the value’s type.
It can block a value so that it never flows any further down the chain.
As a result, what ultimately comes out at the subscriber end of the chain can be something quite different from what went in at the publisher end. Do you remember what I said earlier about what the Combine framework is?
The Combine framework provides a unified publish-and-subscribe API for channelling and processing asynchronous signals.
We have come at last to the “processing” part of that definition.