RxSwift and the Secret of the Variadic DisposeBag

Use one of RxSwift’s newest features to dramatically clean up your code

Michael Long
Feb 8 · 3 min read

My company has been using RxSwift for all new iOS projects for a while now, and we’ve come to appreciate it’s power, flexibility, and conciseness.

That said, there is one area where RxSwift is, shall we say, somewhat less than concise. Where, in point of fact, it’s downright redundant.

So today, let’s talk about Disposables and DisposeBags.

Disposables and DisposeBags?

As I’m sure you’re aware, Disposables and DisposeBags are RxSwift’s concession to Swift’s ARC memory management.

When you subscribe or bind to or drive from a RxSwift Observable, that subscription returns a Disposable. That disposable is basically a reference to that subscription and to that subscription’s entire Observable chain.

Until that disposable is disposed, the subscription chain exists (unless the subscription receives a completed or error event, but that’s another story).

So. Bottom line is that subscriptions return disposables which we need to maintain in order to properly control the lifecycle of the subscription.

Those disposables, for the sake of convenience, are usually inserted into a DisposeBag that’s been created and attached to a UIViewController (or in some cases to a View Model or other object).

And a DisposeBag is exactly what it says it is, a bag (or collection) of disposables.

So how does that help us?

Well, when the view controller is deallocated, its variables (including the bag) are deallocated. When the disposeBag is deallocated, its deinit function calls dispose on all of the disposables it contains.

Those disposables, in turn, release any references they may have to any observables they’re observing which may also, in turn, release their references to their observables, and so on, and so on, up the chain until we’re done.

Everything is properly released, nothing has leaked, and everyone is happy.

Except for one minor problem.

Redundancy

So, let’s say you’re using RxSwift and that you’re using a MVVM (Model-View-ViewModel) architecture.

On this particular screen our view model is exposing a set of observables, and our view controller is binding those observables to a set of labels. Pretty basic stuff.

The code looks like this.

class MVVMViewController: UIViewController {    @IBOutlet weak var firstNameLabel: UILabel!
@IBOutlet weak var lastNameLabel: UILabel!
@IBOutlet weak var addressLabel: UILabel!
@IBOutlet weak var cityLabel: UILabel!
@IBOutlet weak var stateLabel: UILabel!
@IBOutlet weak var zipLabel: UILabel!
private var viewModel = MyViewModel()
private var disposeBag = DisposeBag()
func setupSubscriptions() {
viewModel.firstName
.bind(to: firstNameLabel.rx.text)
.disposed(by: disposeBag)
viewModel.lastName
.bind(to: lastNameLabel.rx.text)
.disposed(by: disposeBag)
viewModel.address
.bind(to: addressLabel.rx.text)
.disposed(by: disposeBag)
viewModel.city
.bind(to: cityLabel.rx.text)
.disposed(by: disposeBag)
viewModel.state
.bind(to: stateLabel.rx.text)
.disposed(by: disposeBag)
viewModel.zip
.bind(to: zipLabel.rx.text)
.disposed(by: disposeBag)
}
}

It’s all very straightforward, but perhaps you’ve noticed a slight bit of redundant, boilerplate code in the above example?

That’s right. Each and every subscription (bind) returns a disposable that has to be added to the disposeBag. Each one returns it. And each one has to be maintained. There’s no getting around it.

Or is there?

Solution? The Variadic DisposeBag

Now check out the following code.

class RxSwiftViewController: UIViewController {    @IBOutlet weak var firstNameLabel: UILabel!
@IBOutlet weak var lastNameLabel: UILabel!
@IBOutlet weak var addressLabel: UILabel!
@IBOutlet weak var cityLabel: UILabel!
@IBOutlet weak var stateLabel: UILabel!
@IBOutlet weak var zipLabel: UILabel!
private var viewModel = MyViewModel()
private var disposeBag = DisposeBag()
func setupSubscriptions() {
disposeBag.insert(
viewModel.firstName.bind(to: firstNameLabel.rx.text),
viewModel.lastName.bind(to: lastNameLabel.rx.text),
viewModel.address.bind(to: addressLabel.rx.text),
viewModel.city.bind(to: cityLabel.rx.text),
viewModel.state.bind(to: stateLabel.rx.text),
viewModel.zip.bind(to: zipLabel.rx.text)
)
}
}

Much cleaner! But what happened to all of the disposables?

Well, RxSwift 4.3 added a variadic version of the DisposeBag’s insert function. And variadic functions can take one — or more — parameters. In this case one or more disposables.

Because if you look inside disposed(by: disposeBag), you’ll find that it’s just one line of code: disposeBag.insert(self).

So instead of taking the result of each subscription and functionally chaining it to disposed(by:), we just bypass the middleman and add each one directly to the disposeBag ourselves.

I think you’ll agree that the code is definitely more concise and that by eliminating some of the boilerplate we can better express the intent of the function.

Enjoy.

By the way, if you find the variadic insert on DisposeBag useful, you’re welcome. It was one of my minor contributions to the RxSwift project. ;)

Michael Long

Written by

Michael Long (RxSwifty) is a Senior Lead iOS engineer at CRi Solutions, a leader in cutting edge iOS, Android, and mobile corporate and financial applications.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade