I am combining two publishers to determine what the center coordinate of a map view should be. The two publishers are:
CLLocationManager
(the first location reported once the CLLocationManager
begins sending location updates).In code:
class LocationManager: NSObject, ObservableObject {
// The first location reported by the CLLocationManager.
@Published var initialUserCoordinate: CLLocationCoordinate2D?
// The latest location reported by the CLLocationManager.
@Published var currentUserCoordinate: CLLocationCoordinate2D?
// What the current map view center should be.
@Published var coordinate: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 42.35843, longitude: -71.05977) // Default coordinate.
// A subject whose `send(_:)` method is being called elsewhere every time the user presses a button to center the map on the user's location.
var centerButtonTappedPublisher: PassthroughSubject<Bool, Never> = PassthroughSubject<Bool, Never>()
// The combined publisher that is where all my troubles lie.
var coordinatePublisher: AnyPublisher<CLLocationCoordinate2D, Never> {
Publishers.CombineLatest($initialUserCoordinate, centerButtonTappedPublisher)
.map { initialCoord, centerButtonTapped in
var latestCoord = initialCoord
if centerButtonTapped {
latestCoord = self.currentUserCoordinate
}
return latestCoord
}
.replaceNil(with: CLLocationCoordinate2D(latitude: 42.35843, longitude: -71.05977))
.eraseToAnyPublisher()
}
private var cancellableSet: Set<AnyCancellable> = []
//... Other irrelevant properties
private override init() {
super.init()
coordinatePublisher
.receive(on: RunLoop.main)
.assign(to: \.coordinate, on: self)
.store(in: &cancellableSet)
//... CLLocationManager set-up
}
}
extension LocationManager: CLLocationManagerDelegate {
//...
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// We are only interested in the user's most recent location.
guard let location = locations.last else { return }
let latestCoord = location.coordinate
if initialUserCoordinate == nil {
initialUserCoordinate = latestCoord
}
currentUserCoordinate = latestCoord
}
//...
}
Both publishers, $initialUserCoordinate
and centerButtonTappedPublisher
, publish updates - I have confirmed this. However, the combined publisher coordinatePublisher
only fires when the "center map on current location" button is tapped. It never fires when the initialUserCoordinate
property is first set.
This question suggests adding a .receive(on: RunLoop.main)
after the Publishers.CombineLatest($initialUserCoordinate, centerButtonTappedPublisher)
but this does not work for me.
What am I doing wrong?
You need to use Publishers.Merge
rather than CombineLatest
, see the documentation:
For Publishers.CombineLatest
:
A publisher that receives and combines the latest elements from two publishers.
For Publishers.Merge
A publisher that emits an event when either upstream publisher emits an event.