iosswiftreactive-programmingreactive-cocoareactive-cocoa-5

Can a signal observer get access to the last emitted value of a ReactiveCocoa signal?


I'm starting using ReactiveCocoa and I'm still struggling with some basic concepts:

  1. My app starts listening for geolocation data (init in my view model)
  2. My app emits a signal with my current location (didFindCurrentPosition is called)
  3. My view controller showing a map loads (viewDidLoad in my view controller)
  4. My view controller starts observing the current location signal (still viewDidLoad)

My problem is: after step 2 is achieved, if no other event is sent on the signal, my view controller doesn't get notified.

How can my view controller get access to the last value from the signal? (ie how to get access at step 3 to a value emitted at step 2?)

Thanks for your help.

PS: ReactiveCocoa looks like a great library but I'm puzzled by the state of the documentation. IMHO, it is not very clear and lacks some clear guides on how to use it.

The Code

The view model:

class MyViewModel: LocationManagerDelegate {
    let locationManager: LocationManager
    let geolocationDataProperty = MutableProperty<Geolocation?>(nil)
    let geolocationData: Signal<Geolocation?, NoError>

    init() {
        geolocationData = geolocationDataProperty.signal

        // Location Management
        locationManager = LocationManager()
        locationManager.delegate = self
        locationManager.requestLocation()
    }

    // MARK: - LocationManagerDelegate

    func didFindCurrentPosition(location: CLLocation) {
        geolocationDataProperty.value = Geolocation(location: location)
    }
}

The view controller:

class MyViewController: UIViewController {
    let viewModel = MyViewModel()

    init() {
        super.init(nibName: nil, bundle: nil)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        viewModel.geolocationData
            .observe(on: UIScheduler())
            .observeValues { geolocation in
                debugPrint("GOT GEOLOCATION")
        }
    }
}

Solution

  • You already have a Property that holds the latest value emitted. Instead of using the property's signal use the producer. That way when you start the producer you will get the current value first (in none was sent you will get nil).

    Cf. the documentation:

    The current value of a property can be obtained from the value getter. The producer getter returns a signal producer that will send the property’s current value, followed by all changes over time. The signal getter returns a signal that will send all changes over time, but not the initial value.

    So, regarding the code in the question, the viewDidLoad method should do something like the following:

    viewModel.geolocationDataProperty
            .producer
            .start(on: UIScheduler())
            .startWithValues { geolocation in
                debugPrint("GOT GEOLOCATION")
        }