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 operator. withLatestFrom will transform an event from the original stream into an event of stream passed to withLatestFrom.

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 resource.

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 resource or buttonTap updates. This is not what we want though. We only want buttonTap to emit events on the observable. We want to transform buttonTap into resource when the event is ready. To do this, we must also use the take operator. 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.

Gotchas

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.

http://reactivex.io/documentation/operators/combinelatest.html

http://reactivex.io/documentation/operators/take.html