So far we’ve:
Today, we’re going to start really leveraging Rx for what it’s best at: eliminating stored state, and thus, preventing avenues for bugs.
Recap
When we left things, our ViewController
looked like this:
class ViewController: UIViewController {
// MARK: Outlets
@IBOutlet weak var label: UILabel!
@IBOutlet weak var button: UIButton!
// MARK: ivars
private let disposeBag = DisposeBag()
private var count = 0
override func viewDidLoad() {
self.button.rx.tap
.debug("button tap")
.subscribe(onNext: { [unowned self] _ in
self.onButtonTap()
}).addDisposableTo(disposeBag)
}
@IBAction private func onButtonTap() {
self.count += 1
self.label.text = "You have tapped that button \(count) times."
}
}
This is fine, but it’s already obvious that something is redundant.
Quick Win
As a first step, let’s get rid of the useless onButtonTap()
. We can just fold
that into the subscribe()
:
class ViewController: UIViewController {
// MARK: Outlets
@IBOutlet weak var label: UILabel!
@IBOutlet weak var button: UIButton!
// MARK: ivars
private let disposeBag = DisposeBag()
private var count = 0
override func viewDidLoad() {
self.button.rx.tap
.debug("button tap")
.subscribe(onNext: { [unowned self] _ in
self.count += 1
self.label.text = "You tapped that button \(self.count) times."
}).addDisposableTo(disposeBag)
}
}
This is definitely an improvement, but there’s still many more things that we can do in order to make things better.
Scan
In the first part of this series, I said:
Rx brings a bunch of tools to the table; some are the ones we already know. Some are totally new. Once you get to know the tools, it’s easy to reason about what is being done to a stream.
Today we’re going to add a new tool to our toolbox: scan
. The scan
function is summarized on the official Rx site as:
Apply a function to each item emitted by an Observable, sequentially, and emit each successive value.
What?
In the documentation, there’s an interesting tidbit that sounds vaguely familiar:
This sort of operator is sometimes called an “accumulator” in other contexts.
This is still confusing though. Let’s see if we can make sense of this by checking out the marble diagram:

Now this “accumulator” seems to make sense. With each new input (1
, 2
, 3
,
4
, 5
), we seem to be adding that value to the sum of all previous values.
1
+ 0 = 12
+ 1 = 33
+ 3 = 64
+ 6 = 105
+ 10 = 15
Suddenly accumulator makes more sense. In fact, this looks very similar to
vanilla Swift’s reduce()
function.
Using Scan
So, how do we actually use scan
? Looking at the way it’s declared gives us a hint:
public func scan<A>(_ seed: A, accumulator: @escaping (A, Self.E) throws -> A) -> RxSwift.Observable<A>
It looks like we provide a seed
value, and then a closure to perform whatever
accumulation we’d like. What if we seed the scan
with 0
? Rather than
overwriting what we have, let’s do this separately:
let o = self.button.rx.tap
.scan(0) { (priorValue, _) in
return priorValue + 1
}
Now, using Xcode’s quick info popover (⌥-Click), we can see what type o
is:

Awesome! We’ve now got an Observable<Int>
, which theoretically means we have
an Observable
that will signal the current tap count every time the button is
tapped. Perfect!
Aside: I find it’s often useful to break one Observable
chain up as we’ve
done above, and leverage Swift’s type inference to ensure that I’m operating
on the types I expect. In this contrived example, it’s pretty obvious what’s
happening, but in more complex cases—especially when we begin combining
streams—things get hairy quickly.
Additionally, the Swift compiler’s type inference often gets tripped up once you get to combining many different streams and mutating them. One easy way to cheat and get back into the compiler’s good graces is to split up these calls, and if necessary, annotate the types as required.
Wiring Up
Let’s dump our temporary variable o
and use this scan
. Further, let’s also
add a couple debug()
s for good measure. Our new chain now looks like this:
self.button.rx.tap
.debug("button tap")
.scan(0) { (priorValue, _) in
return priorValue + 1
}
.debug("after scan")
.subscribe(onNext: { [unowned self] currentCount in
self.label.text = "You have tapped that button \(currentCount) times."
})
.addDisposableTo(disposeBag)
Notice something important here: we have two calls to debug()
:
- Our existing one that hangs off of
button.rx.tap
- A new one that hangs off of the
scan()
Let’s run the app, tap three times, then look at both the screen and output. I’d drop an animated GIF of the screen behavior, but it looks the same as it always did, which is a victory. Looking at the console output, I’ve added some newlines for clarity, but otherwise haven’t changed anything:
2016-12-16 21:55:12.661: after scan -> subscribed
2016-12-16 21:55:12.662: button tap -> subscribed
2016-12-16 21:55:15.807: button tap -> Event next(())
2016-12-16 21:55:15.807: after scan -> Event next(1)
2016-12-16 21:55:16.728: button tap -> Event next(())
2016-12-16 21:55:16.728: after scan -> Event next(2)
2016-12-16 21:55:17.628: button tap -> Event next(())
2016-12-16 21:55:17.628: after scan -> Event next(3)
Notice that not only are there two entries per event on the button.rx.tap
Observable
, one for each debug()
call, but they are showing different
“payloads”. The first is the ()
/Void
we discussed last time.
The second is the Int
that comes out of scan
. Pretty neat.
It’s important to remember that debug()
shows you the state of the stream at
that point in the composition. Sprinkling debug()
calls throughout one
composition, as we have done above, can tell you loads of information about
your transformations.
Dropping State
Did you notice what else is different about the above? Before our scan
, the
subscribe()
closure looked like this:
self.count += 1
self.label.text = "You tapped that button \(self.count) times."
After scan
:
self.label.text = "You have tapped that button \(currentCount) times."
We’ve dropped our dependency on the count
instance variable. In turn, this
means we have no stored state in this class anymore. We can now safely
remove count
from the class entirely.
Now, Rx is starting to spread its wings: we have removed stored state, and as such, we’ve removed a potential avenue for bugs. Awesome! 🎉
You can see this version of the code at this commit.
Small Improvement
Though not strictly necessary, let’s demonstrate how we can split things out slightly. Sometimes it helps to split a compound operation into multiple individual operations for code clarity. Remember: clever code is hard to maintain code.
In our case, let’s leverage map
to split out the generation of the message
that is being sent to the UILabel
:
self.button.rx.tap
.debug("button tap")
.scan(0) { (priorValue, _) in
return priorValue + 1
}
.debug("after scan")
.map { currentCount in
return "You have tapped that button \(currentCount) times."
}
.debug("after map")
.subscribe(onNext: { [unowned self] newText in
self.label.text = newText
})
.addDisposableTo(disposeBag)
In this more verbose version of our Observable
chain, it’s abundantly clear
what is happening:
- We’re reacting to events on a
UIButton
’stap
stream - We’re
scan
ning it to reduce it to a singleInt
- We’re
map
ping thatInt
and creating aString
from it - We’re
subscribe()
ing to thatObservable<String>
and setting thatString
on aUILabel
.
Yes, it’s quite a bit longer than the procedural version we started with.
That’s a bummer, but in a contrived example like this one, it’s to be expected.
Most systems using Rx are not this simple, and have far more wild things going
on. Thus, in more complex examples of Observable
streams, the savings are often
considerable.
Regardless of how long it is, I’d argue this is actually an extremely approachable
block of code. As I said above, it’s eminently clear what is happening. It’s clear
what the input is, and how we’re mutating it to get to our final subscribe()
.
It’s so much easier to reason how this code works, since there’s no reliance on
other functions, and no reliance on Interface Builder wiring.
Since we examined the console output from the scan
, let’s take a look at how
it looks with our newly introduced map
. This time, I’ll only tap the button
once, and I will continue to add newlines for clarity:
2016-12-16 22:13:41.517: after map -> subscribed
2016-12-16 22:13:41.518: after scan -> subscribed
2016-12-16 22:13:41.519: button tap -> subscribed
2016-12-16 22:13:45.766: button tap -> Event next(())
2016-12-16 22:13:45.766: after scan -> Event next(1)
2016-12-16 22:13:45.768: after map -> Event next(Optional("You have tapped that button 1 times."))
As exepected, through the various debug()
calls, we can trace our button tap
from ()
→ 1
→ You have tapped the button 1 times.
Very neat.
You can see this version of the demo app at this commit on Github.
Next Steps
We’ve now had a big win and removed all stored state in our ViewController
.
Next time, we’ll take things a little bit further, and leverage one of the
higher order classes that RxCocoa makes available to us: the Driver
. We’ll also
clean things up a bit, and then take stock of where we’ve landed.