ReactiveSwift Simple Example
Asked Answered
H

1

11

I've read the documentation, gone through their wonderful Playground example, searched S.O., and reached the extent of my google-fu, but I cannot for the life of me wrap my head around how to use ReactiveSwift.

Given the following....

class SomeModel {
    var mapType: MKMapType = .standard
    var selectedAnnotation: MKAnnotation?
    var annotations = [MKAnnotation]()
    var enableRouteButton = false

    // The rest of the implementation...
}

class SomeViewController: UIViewController {

    let model: SomeModel
    let mapView = MKMapView(frame: .zero) // It's position is set elsewhere
    @IBOutlet var routeButton: UIBarButtonItem?

    init(model: SomeModel) {
        self.model = model
        super.init(nibName: nil, bundle: nil)
    }


    // The rest of the implementation...
}

....how can I use ReactiveSwift to initialize SomeViewController with the values from SomeModel, then update SomeViewController whenever the values in SomeModel change?

I've never used reactive anything before, but everything I read leads me to believe this should be possible. It is making me crazy.

I realize there is much more to ReactiveSwift than what I'm trying to achieve in this example, but if someone could please use it to help me get started, I would greatly appreciate it. I'm hoping once I get this part, the rest will just "click".

Hardness answered 28/11, 2016 at 3:27 Comment(0)
T
19

First you'll want to use MutableProperty instead of plain types in your Model. This way, you can observe changes to them.

class Model {
    let mapType = MutableProperty<MKMapType>(.standard)
    let selectedAnnotation = MutableProperty<MKAnnotation?>(nil)
    let annotations = MutableProperty<[MKAnnotation]>([])
    let enableRouteButton = MutableProperty<Bool>(false)
}

In your ViewController, you can then bind those and observe those however necessary:

class SomeViewController: UIViewController {

    let viewModel: Model
    let mapView = MKMapView(frame: .zero) // It's position is set elsewhere
    @IBOutlet var routeButton: UIBarButtonItem!

    init(viewModel: Model) {
        self.viewModel = viewModel
        super.init(nibName: nil, bundle: nil)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        routeButton.reactive.isEnabled <~ viewModel.enableRouteButton
        viewModel.mapType.producer.startWithValues { [weak self] mapType in
            // Process new map type
        }
        // Rest of bindings
    }
    // The rest of the implementation...
}

Note that MutableProperty has both, a .signal as well as a .signalProducer. If you immediately need the current value of a MutableProperty (e.g. for initial setup), use .signalProducer which immediately sends an event with the current value as well as any changes.

If you only need to react to future changes, use .signal which will only send events for future changes.

Reactive Cocoa 5.0 will add UIKit bindings which you can use to directly bind UI elements to your reactive layer like done with routeButton in the example.

Tuberculin answered 28/11, 2016 at 9:0 Comment(2)
That "click" you just heard was everything making sense after I read your answer. Thank you for breaking it down in the example, as it made all the difference!Hardness
Glad I could helpTuberculin

© 2022 - 2024 — McMap. All rights reserved.