Reputation: 7439
I'm starting using ReactiveCocoa and I'm still struggling with some basic concepts:
init
in my view model)didFindCurrentPosition
is called)viewDidLoad
in my view controller)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 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")
}
}
}
Upvotes: 4
Views: 2235
Reputation: 582
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")
}
Upvotes: 5
Reputation: 5190
If your problem is to have old value when it gets modified .You can try with Key-Value Observing . Add your property for value observation like
geolocationDataProperty.addObserver(self, forKeyPath: "value", options: [.new, .old], context: &observerContext)
Whenever your geolocationDataProperty gets modified ,you will receive that value in delegate method
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
let newValue = change?[.newKey] as? NSObject
let oldValue = change?[.oldKey] as? NSObject
//Add your code
}
Upvotes: 0
Reputation: 2089
Bindings from any kind of streams of values can be crated using the <~ operator but you can start to modify your code in the following way and see it is working fine, it is easier to debug :-)
class MyViewModel: LocationManagerDelegate {
let locationManager: LocationManager
let geolocationDataProperty = MutableProperty<Geolocation?>(nil)
init() {
geolocationDataProperty.signal.observeValues { value in
//use the value for updating the UI
}
// Location Management
locationManager = LocationManager()
locationManager.delegate = self
locationManager.requestLocation()
}
// MARK: - LocationManagerDelegate
func didFindCurrentPosition(location: CLLocation) {
geolocationDataProperty.value = Geolocation(location: location)
}
}
then we can try to use the binary operator for implementing the MVVM pattern, since having direct reference to the UI element inside the modelview is definitely a bad idea :-)
Take a look at this article, it is really interesting.
I am not sure if your map component is directly supporting the reactive framework.
see it has the ".reactive" extension and if it can used for updating the current position property
if it is present then the <~ operator can be used writing something similar to
map.reactive.position <~ viewModel.geolocationDataProperty
if it does not have the reactive extension then you can simply move this code in your viewcontroller
viewModel.geolocationDataProperty.signal.observeValues { value in
//use the value for updating the UI
}
Upvotes: 0