Custom property bindings with RxSwift

Recently, in one of the few meetups I’ve been in person to, someone told me the didn’t try RxSwift because somebody told them that RxSwift doesn’t support custom views.

While I’m still baffled what that original person meant, I thought the best way to disagree is to write a short tutorial to prove them wrong.

What are custom property sinks?

When you use RxCocoa bindings you can easily bind the values an observable emits to some view on screen like so:

myObservable
  .map { "new value is \($0)" }
  .bind(to: myLabel.rx.text )
  .disposed(by: bag)

But have you ever wondered what rx.text is and what so magical is there about it? Nothing. There’s nothing magical - just Cmd click on it and you will see the source code:

extension Reactive where Base: UILabel {
    /// Bindable sink for `text` property.
    public var text: UIBindingObserver<Base, String?> {
        return UIBindingObserver(UIElement: self.base) { label, text in
            label.text = text
        }
    }
}

The extension adds the property text to the Reactive struct (this is effectively the rx property on classes with reactive extensions) but only to the class UILabel.

text itself is of type UIBindingObserver - it’s simply an observer similar to any other, that receives values and decides how to process them.

Note: In RxSwift 4 UIBindingObserver has been changed to Binder.

When you bind an observable subscription to the text property, the property returns a new observer which executes its block parameter when each value is emitted. E.g. any time it receives a new value it runs the code label.text = text.

That’s literally all there is to adding a bindable sink property to a class - in your code you might be binding a UIColor value instead of String, or adding a reactive extension to UIApplication instead of UILabel like in the example above, but the very simple demonstrated technique stays the same.

Adding a reactive extension to SwiftSpinner

To show the code in action let’s add a quick reactive extension to the SwiftSpinner library.

I’m creating a new Xcode project and importing all the pods I need for the demo:

import SwiftSpinner
import RxSwift
import RxCocoa

First I’m adding a reactive extension on the SwiftSpinner class:

extension Reactive where Base: SwiftSpinner {
  
}

This will add an rx property to SwiftSpinner instances. Further I’ll add the property progress so I can bind observables emitting Int values to it:

public var progress: UIBindingObserver<Base, Int> {

}

Finally I’m converting values between 0 and 100 and call SwiftSpinner.show(progress:title:) to display the current progress on screen and the complete code is:

extension Reactive where Base: SwiftSpinner {
    public var progress: UIBindingObserver<Base, Int> {
        return UIBindingObserver(UIElement: self.base) { spinner, progress in
            let progress = max(0, min(progress, 100))
            SwiftSpinner.show(progress: Double(progress)/100.0, title: "\(progress)% completed")
        }
    }
}

And that’s it. Let’s test the new reactive extension:

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    Observable<Int>.timer(0.0, period: 0.15, scheduler: MainScheduler.instance)
        .bind(to: SwiftSpinner.sharedInstance.rx.progress )
        .disposed(by: bag)
}

Let’s see that timer drive the progress bar like a mad man:

And that’s a wrap! If anyone is misinformed about how easy it is to create reactive extensions with RxSwift send them read this post!

Where to go from here?

Dig into the source code of RxCocoa - there’s no better way to learn about creating bindable sinks. Or even better - get our book about RxSwift :)

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

Share this post:


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.
Tags// , , , ,