Timing and Transforming observables in RxSwift
I often find myself in a situation where I have a stream of button taps that I want to transform into the latest event from another stream. This can be done conveniently using the
withLatestFrom will transform an event from the original stream into an event of stream passed to
Say we have a button that emits a stream of
Void on each tap, and we want to transform that into the latest value from a stream of
Bool. We can use the
withLatestFrom operator like so:
Now we can use the result from
simpleCombineStreams to perform an operation with our resource on each button press. This stream will always have the latest value from resource, so if new events come in while the user was busy doing other things, we still have fresh data.
When the timing does not quite work
Ideally, when our user taps a button, our application responds quickly. If we present a button to the user which needs data that is not ready, our
buttonTap may emit before our
resource observable has produced a single event. In
simpleCombineStreams this would cause
buttonTap to drop all events that arrive before the first event received by
Instead of dropping events, we want to construct a stream that will emit when at least one event has been received by
resource. Out of the box
withLatestFrom cannot provide this functionality. However we can implement it using two more Rx operators!
combineLatest takes a collection of streams and creates one new stream that will emit whenever one of the underlying streams emit. The stream created from
combineLatest will only emit once all of the streams in the collection have emitted at least one element.
We can use
withLatestFrom to create a stream that emits a
(Void, Bool) tuple whenever
buttonTap updates. This is not what we want though. We only want
buttonTap to emit events on the observable. We want to transform
resource when the event is ready. To do this, we must also use the
take will limit an observable to only emit a specified number of events. We can use
take to ensure that we wait for the first
resource event, then let
buttonTap provide all subsequent events.
Once we wait for all events to be ready, we can tack on a
withLatestFrom operator to properly transform the stream. Since we waited for
resource to be ready, we know that
buttonTap will no longer drop events.
Beware of using this technique with fail-able operations. In our theoretical scenario, we do not handle a case where
resource never emits an event. If that were the case our stream would never emit and our button would appear unresponsive. This technique is best used when we know a resource will arrive eventually, but it is simply not ready yet.