RxSwift’s Many Faces of FlatMap

Daniel Tartaglia
3 min readFeb 3, 2019

With most types that have a flatMap method (for example Array and Optional) there is no time element involved and thus there is only one way to flatten them, but RxSwift’s Observable type has a time element which gives us more possibilities on how to flatten a group of them. In this article we will explore the four different ways of flattening an Observable.

The Value of flatMap

The flatMap method makes it easy for us to do a couple of different things that would have been hard to do otherwise. First, it allows us to increase the number of events being produced by an Observable stream. Second, it allows us to perform some asynchronous work as part of the stream.

For an example of the first idea we have this:

let string = Observable.of("a", "b", "c")
string
.flatMap { Observable.of("\($0)-x", "\($0)-y", "\($0)-z") }
.subscribe(onNext: { element in
print(element) // this prints 9 values.
})

An example of the second idea is something like this:

string
.flatMap { URLSession.shared.rx.data(request: URLRequest(url: URL(string: "http://myserver.com/api/get_user?id=\($0)")!)) }
.subscribe(onNext: { element in
print(element) // this prints the result of the 3 network requests.
})

The RxSwift library provides four ways to flatten observables and it’s important to understand the differences.

The Choices and Their Differences

The RxSwift library gives us four different flatMap methods:

flatMap

The basic flatMap merges the results from all the sub-Observables into one Observable. Every time the source emits a value, flatMap will create a new sub-Observable and subscribe to it, merging results of all the sub-Observables into one. There is no guarantee about the order of the outputs. If any of them emit an error, it will immediately shut down all the others and emit the error. The operator won’t emit a completed event until the source and all the sub-Observables have emitted completed events.

flatMapLatest

This is probably the most commonly used flatMap. When its source emits the first value, flatMapLatest will create a new sub-Observable and emit its next events. Unlike the flatMap above, if the source emits another value before the sub-Observable completes, then flatMapLatest will dispose the current sub-Observable before starting the new one.

It’s the cancelation ability that makes this operator so popular. If you don’t need the results of the network request anymore because the inputs have changed, then flatMapLatest is what you want.

flatMapFirst

Occasionally, you don’t want to cancel the sub-Observable and instead you would rather ignore events from the source until the sub-Observable is done. This is what flatMapFirst is for. Like flatMapLatest, it only allows one sub-Observable at a time. Unlike flatMapLatest, it will not cancel an ongoing sub-Observable. Instead it ignores events from the source until the sub-Observable is done.

concatMap

Like flatMap, the concatMap operator will create a sub-Observable for every event from the source, but this one guarantees the order of the output. It will only subscribe to one sub-Observable at a time and won’t subscribe to the next sub-Observable until the current one completes. Unlike flatMapFirst, it won’t ignore other events, instead is will buffer them to use when it’s ready.

Summary

These four flatMap operators give us a lot of flexibility to handle events. We can merge them all together, track the most recently emitted one, track the first one and ignore other events until it’s done, or track one at a time and buffer other events in a queue for later use. What else could you want?

--

--

Daniel Tartaglia

I started programming as a hobby in the late ’70s and professionally in the late ’90s. I’ve been writing iOS apps since 2010 and using RxSwift since 2015.