Building a custom `sample` operator
In Simple custom Combine operators I spoke about how you can cheaply create your own custom Combine operator without the need to create a new custom Publisher
type.
In this post I’d like to show how this kind of “cheap” operators could actually be quite complex and useful; still being created only by creatively combining existing operators.
Let’s look at sample(_)
…
The sample
operator
rxmarbles.com is a marveleous website where you can find visual and interactive presentations of a multitude of reactive operators (based in their implementation in RxJS).
One operator from Rx that I really like but I still don’t see implemented in Combine is sample
.
sample
is very handy and you can see it in action here: (https://rxmarbles.com/#sample). It allows you to take the values of a publisher only at given times when a second publisher (a source or a trigger) emits.
The second publisher’s values are ignored - it only serves as a “trigger” as to when should the latest value of the first publisher be emitted.
A practical application of sample
is having a timer but you only take the current value when a button is pressed like so:
Now let’s look at how to quickly re-implement sample
by combining built-in Combine operators.
Re-implementing sample
in Combine
First we will start with defining an extension on Publisher
- again we will not create a new Publisher
implementation but rather extend the protocol with a “cheap” operator.
|
|
sample
will operate on the values of the current publisher. The method takes a single parameter which is the “trigger” or “source” publisher.
The source publisher needs to meet a couple of conditions:
- first, the values emitted by
source
need to be, regardless of their actual type,Equatable
so we can check for a new value by comparing the current one to the last one. - second, the
Failure
types ofsource
andself
need to match as this is a requirement ofcombineLatest(_)
which we will use in the method body.
Now let’s see about the imlementation. First we will start by combinging the two publishers together via combineLatest
:
|
|
That will combine the values of self
and source
into a single stream of tuples, each containing the latest values of both publishers.
Next we will filter any repeating values of the source
publisher by removing duplicates via a custom closure:
|
|
This will remove any values where the source publisher hasn’t emitted a new value.
Finally we need to get rid of the extra value in the tuple: the source
element which is of no interest for this operator. And also erase the operator type to remove some of the complexity of the return type:
|
|
And the final implementation is:
|
|
We recreated sample
as implemented in Rx by combinin/g combineLatest
, removeDuplicates
, and a map
. Now we could use sample
in all kinds of use-cases (though I personally find it most practical when using UI controls for the source). Let’s try a quick and practical test.
Getting sample
out for a spin
A really simple test scenario could be:
- first publisher is a counter that emits consequtive integer values,
- second publisher (source, which could be a UI button or a timer) emits periodically to output the current counter value.
Let’s first get the counter going:
|
|
The counter (when subscribed) will continuously count like so:
1
2
3
4
5
...
Then add a timer firing each 5 seconds to use as the “source”:
|
|
And finally use sample
to use the two publishers together:
|
|
That will print the current value of counter
each time trigger
fires:
29
58
88
117
147
176
205
...
All works as expected so that’s a wrap for today!
Where to go from here?
You saw how to create more useful and reusable but “cheap” to make Combine operators. Next you’d probably want to look into creating custom operators via implementing new Publisher
types!
To learn more about implementing custom operators and publishers check out Combine: Asynchronous programming with Swift - this is where you can see all updates, discuss in the website forums, and more.