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
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
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 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.
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
sourceneed to be, regardless of their actual type,
Equatableso we can check for a new value by comparing the current one to the last one.
- second, the
selfneed to match as this is a requirement of
combineLatest(_)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
That will combine the values of
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:
sample as implemented in Rx by combinin/g
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.
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
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
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.