RxSwift let’s you update your views dynamically in your app in many ways – many of which can get messy over time, as I’ve learned the hard way.

The approach that I got used to in order to update views dynamically (and without resorting to crash-prone key-value-observation) was to pass Observables to the views. The views then subscribed to them and cleaning up o their own using a DisposeBag. It worked, but closely coupled the views to RxSwift.

RxSwift has a much cleaner way for doing this – a Binder. You might be familiar with them through helpers such as myLabel.rx.text to which you can bind your Observables. After using that for a while, I wondered how to expose similar properties for custom views and found the answer.

It’s very simple:

  1. Implement functions to update your view with regular input data as you would do in a non-reactive world.
  2. Create a computed property on the view (ideally using a reactive extension) which exposes a Binder.
  3. In your view controller, bind your model to that binder (and clean it up there).

Say, you have a tachometer model with a Driver<Measurement<UnitSpeed>> (regular Observable works similar) which has a speed value, that you want to bind to a tachometer view.

Your view might have an update function such as:

class TachometerView: UIView {
 func update(with speed: Measurement<UnitSpeed>) {
    speedLabel.text = speedFormatter.string(from: speed)

Now create a binder like this:

import RxSwift
import RxCocoa

extension Reactive where Base: TachometerView {
  var speed: Binder<Measurement<UnitSpeed>> {
    return Binder(self.base) { view, speed in
      view.update(with: speed)

And then you can use it like this:

class MyViewController: UIViewController {
 override func viewDidLoad() {
    // Create + configure view model, call super, ...
      .disposed(by: disposeBag)

This helps clean-up views and view controllers, and leads to more declarative code, which is great for maintainability.