Combining Observables: combineLatest, withLatestFrom, zip

When it comes to writing an application in the reactive way, sooner or later your output will depend on more than just one Observable sequence. ReactiveX offers multiple operators to combine multiple Observables into one sequence.

Today I’ll describe what are the differences between combineLatest, withLatestFrom, zip and what are good scenarios to use them. Enjoy reading 💪📚


combineLatest

combineLatest is an operator which you want to use when value depends on the mix of some others Observables. The easiest sample would be a isEnabled Observable if a button should be enabled or not.

Assume you have a login form. The e-mail provided by user needs to match a regex and the password must have more than 8 characters length.

What you want to achieve is to make the button enabled only when these 2 requirements are met. This is a typical use case for combineLatest. It takes multiple Observables and emits an event which is the result of connecting latest values in these Observables:

The strange closure above { $0 && $1 } is the resultSelector argument which is the function. Duty of resultSelector is to map a Observables next events tuple into the next event of the operator. The order of values in resultSelector is the same as the order of the input Observables. All the operators described in this article use resultSelector as their last arguments.

isButtonEnabled will always reflect if the requirements are met or not. Whenever isEmailVaild or isPasswordValid changes its state the isButtonEnabled will also send a new value based on current state.


What’s you need to remember is the combineLatest will send his first event only if all of its input Observables have ever sent a value.

I think it’s intuitive since the latest part in the name. How would you like to send a latest value if the latest value hasn’t been sent yet?
Sometimes it is a good idea to use startWith() in pair with combineLatest.

Sending a login request

So… you did a validation for the credential. Now you want to send the login request on button tap. How can you do that?

The most trivial solution is to read current values of email & passwords just before invoking the request:

However, such the approach isn’t the best one. First of all, it’s not reactive 😈 and it forces you to use [weak self]. You can get rid of the capture list by switching to a reactive solution.

combineLatest 💡? 🤔

Maybe your first idea is to use combineLatest from 3 Observables: the button tap, e-mail, and the password.

👏 you got rid of nasty capture list.

However, it’s better to avoid using combineLatest for such types of features.

combineLatest sends next event when any of inner Observables emits a new event. It means, changing the e-mail or password can also trigger the loginUseCase.

When you write programs in the reactive way you need to always think what should be a trigger for the action. In our case, it’s the button responsibility only. It means you shouldn’t use combineLatest with the button.rx.tap inside.

withLatestFrom

withLatestFrom is the operator which solves the problem. It takes an Observable as an input and transforms the trigger into the latest event from the input Observable.

Now you can listen for button taps and transform them into credential:


withLatestFrom comes in 2 forms: with and without the resultSelector. If you use the version without the resultSelector, the output type of an Observable will be the type of the Observable passed into the withLatestFrom.

withLatestFrom & UITableView selection

withLatestFrom it’s also handy when you deal with UITableView. When you display items in the UITableView usually you have to handle row taps somehow. UITableView gives you the indexPath of the selection, so you have to map it into the model object:

zip

zip is a similar operator to combineLatest. However, zip always creates pairs from events with the same indexes. If you have two Observables, zip will wait for the new events in both the Observables. It’s easier to understand that on a diagram:


zip is useful when you want to invoke 2 (or more) API requests in parallel but you need to wait for all of them to finish.

Actually, I’ve already shown you the usage of zip in this post.

Summary

combineLatest is a great operator to calculate a state variable like isEnabled.

The difference between combineLatest & zip is zip doesn’t save previously sent elements. Even if Observable A sends a new event zip still waits for the new event from Observable B.

On the other hand, in the same scenario, combineLatest would just take the latest value from B.

withLatestFrom is an operator to be used with triggers for some actions. When you write an application in the reactive way it’s important to think what’s the trigger for your action. Usually, combineLatest is not the operator you are looking for since it will pop too often.

References

Title image – flickr.comTerence J sullivan

You Might Also Like