combineLatest beyond the basics
19/May 2017
In the last couple of weeks I got to talk to few people who were either still struggling to understand what exactly does combineLatest
do or didn’t know about a more recent power feature.
So here it is … a post about combineLatest
!
What does the combineLatest operator do?
combineLatest
is super useful and it was the first thing in RxSwift that really made see how powerful Rx is and showed me what kind of problems can be easily solved with RxSwift.
So what does it do? Any time any of the source sequences emits an element, also combineLatest
’s result seqiemce emits an element, which contains all the latest values of each of the source sequences.
Let’s look at a marble diagram (source: http://rxmarbles.com/#combineLatest):
We have two sequences S1 and S2, which emit values - the former emits numbers and the latter - letters.
After all source sequences emit for the first time (e.g. they do have a latest value), when any of the sources emits an element combineLatest
grabs the latest values of all sources and emits the combined result.
So let’s have a look what happens on the diagam above:
- first, S1 emits
1
- since not all sources have emitted at that time, the result does not emit - then, S1 emits
2
- same thing happens since S2 still hasn’t emitted anything (e.g. has no latest value) - then S2 emits
A
- at that point all sources have a latest element socombineLatest
takesA
and combines it with the latest element from S12
. (“What about1
?” some of you are asking. Well - tough luck,1
didn’t make it.) - later S2 emits another element
B
, againcombineLatest
combines it with the latest element from S1, which is2
- and so forth…
combineLatest
is particularly useful handling UI input (you can react to all changes in any of the UI’s text fields, switches, etc) or when you fire a number of networking requests and need to react when any of the http responses completes.
Using combineLatest with a dynamic number of source observables
I created a small example for this article, and added the code to my https://github.com/icanzilb/RxSwiftoniOS repository. If you clone the repo and run it - it’s under number 4 in the main menu.
If you’re only interested in the code just peak inside here: https://github.com/icanzilb/RxSwiftoniOS/blob/master/RxSwiftiOS/CombineViewController.swift.
One of the lesser known features of combineLatest
is that you can either use it with a given number of sources (which can be two, three, four, or more but still a fixed number), like so:
Observable.combineLatest(S1, S2, S3) { value1, value2, value3 in
print("latest values: ...")
}
But if you don’t know how many sources you have in advance or in case you want to be able to easily map over the emitted values you can also provide a collection of source observables to combineLatest
. Like so:
Observable.combineLatest(sources) { values in
print(values.map { "value: $0" })
}
So in the example I wrote for this article I’m fetching JSON with the list of my followers from GitHub and show them in a collection view. For each follower object in the JSON response I fetch their avatar image and show it in a collection cell.
Let’s start with fetching the followers JSON:
Observable.just("https://api.github.com/users/icanzilb/followers")
.map { url in
let apiUrl = URLComponents(string: url)!
return URLRequest(url: apiUrl.url!)
}
.flatMapLatest { request in
return URLSession.shared.rx.json(request: request)
.catchErrorJustReturn([])
}
.map { json -> [User] in
guard let users = json as? [JSONObject] else { return [] }
return users.flatMap(User.init)
}
This code makes a request to https://api.github.com/users/icanzilb/followers
and gets back a list of JSON objects, then it maps them to an array of User
objects. Each User
has a login
and an avatarUrl
properties.
I bind the users list to the followers
Variable and will map each user object to an HTTP request, which will fetch their avatar image from GitHub:
followers.asObservable()
.map { users -> [Observable<Data>] in
return users.map { user in
let request = URLRequest(url: URL(string: user.avatarUrl)!)
return URLSession.shared.rx.data(request: request)
.catchErrorJustReturn(UIImage.blank)
}
}
The map
above takes in the list of user objects [User]
and maps those to URLSession
observable responses [Observable<Data>]
.
Neat! I’m passing the array of observables directly to combineLatest
and convert the returned collection of Data
values into images:
.flatMap(Observable.combineLatest)
.map { $0.map(UIImage.fromData) }
I had to add a quick extension to UIImage
to add a static method to make images out of data values:
extension UIImage {
static func fromData(data: Data) -> UIImage { return UIImage(data: data)! }
}
Nice. Now when all my HTTP requests get their response combineLatest
gives me a list of data values, which I turn into images and can bind to a variable or otherwise make use of.
If you run the linked above demo project you will see that the code fetches all followers from GitHub, then fires off a bunch of requests, gets all their avatars and once that’s done it does a single data reload on the collection view and shows them all at once:
Ok, that’s all about using combineLatest
with a collection of source sequences. But wait - there’s more to be said on the topic …
All at once vs. as they come
When using combineLatest
(especially with network requests) you might want to have two very different use cases.
In the example above you saw how to fire simulatenously an arbitrary number of network requests and only once all of them have completed emit the result of all of them.
But how about if you didn’t want ot wait for all requests to complete? What if you preferred to update the UI as each of the requests receives a response?
Well, remember the discussion above? combineLatest
does emit for each element from the source sequences but only after all of them have a latest value.
So, I wanted to show that as well in the demo project so you will actually find two different methods in the view controller source code getAllAtOnce()
and getAsTheyCome()
. You can switch between those in the app UI by toggling the segment control at the top of the screen.
So the only difference in the code of the two methds (and I mean the only) is that when I’m reloading the collection whenever each of the avatar requests completes I’m providing a default value for each observable.
So here’s the code to fetch all at once:
.map { users -> [Observable<Data>] in
return users.map { user in
let request = URLRequest(url: URL(string: user.avatarUrl)!)
return URLSession.shared.rx.data(request: request)
.catchErrorJustReturn(UIImage.blank)
}
}
And here’s the code that makes combineLatest
’s result emit as each response comes in:
.map { users -> [Observable<Data>] in
return users.map { user in
let request = URLRequest(url: URL(string: user.avatarUrl)!)
return URLSession.shared.rx.data(request: request)
.startWith(UIImage.blank)
.catchErrorJustReturn(UIImage.blank)
}
}
Yes, I’m sure you spotted the difference - the source has a default value (in my case it’s an empty avatar image). This way the collection starts out with a default image for each follower and then as the images are fetched one by one the collection reloads to display each new one. (Yes, not optimal but the point here is to discuss combineLatest.)
So the screen starts with all sources’ latest values (at first those are the default images):
And then you can see how the collection keeps reloading to display each of the received images:
To try this in the app tap on the As they come segment on top.
And that’s a wrap!
Where to go from here?
Once more, the demo project for this article you will find on GitHub here: https://github.com/icanzilb/RxSwiftoniOS.
I hope this post has been helpful and has shown you a couple of nice combineLatest
features. Of course these few examples don’t cover everything - if you’d like to go deeper into combining operators you’re welcome to check Chapter 9, “Combining Operators” and Chapter 10, “Combining Operators in Practice”, which cover far more ground than this post.
The book is available at http://raywenderlich.com/store - this is where you can see any updates, discuss in the website forums, etc.
Hope that post was helpful, and if you want to get in touch you can find me here Follow @icanzilb
Share this post: Tweet

If you'd like to learn how to create professional production apps with RxSwift, the best resource out there is the RxSwift book written by Florent Pillet, Junior Bontognali, Marin Todorov, & Scott Gardner.
It features 20+ chapters covering the basics, the Rx operators, and advanced topics like testing, error handling, and app architecture.
Available from Ray Wenderlich: » Learn more.