Swift Combine 的 CombineLatest 不会响应对其发布者之一的更新而触发
Swift Combine's CombineLatest does not fire in response to an update to one of its publishers
我正在合并两个发布者以确定地图视图的中心坐标应该是多少。这两家出版商是:
- 用户的初始位置由
CLLocationManager
确定(CLLocationManager
开始发送位置更新后报告的第一个位置)。
- 点击 "center map on current location" 按钮时用户的当前位置。
在代码中:
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
}
//...
}
$initialUserCoordinate
和 centerButtonTappedPublisher
这两个发布者都发布了更新 - 我已经确认了这一点。但是,组合发布者 coordinatePublisher
仅在点击 "center map on current location" 按钮时触发。当 initialUserCoordinate
属性 首次设置时它永远不会触发。
建议在 Publishers.CombineLatest($initialUserCoordinate, centerButtonTappedPublisher)
之后添加一个 .receive(on: RunLoop.main)
但这对我不起作用。
我做错了什么?
需要使用Publishers.Merge
rather than CombineLatest
,查看文档:
对于Publishers.CombineLatest
:
A publisher that receives and combines the latest elements from two publishers.
对于Publishers.Merge
A publisher that emits an event when either upstream publisher emits an event.
我正在合并两个发布者以确定地图视图的中心坐标应该是多少。这两家出版商是:
- 用户的初始位置由
CLLocationManager
确定(CLLocationManager
开始发送位置更新后报告的第一个位置)。 - 点击 "center map on current location" 按钮时用户的当前位置。
在代码中:
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
}
//...
}
$initialUserCoordinate
和 centerButtonTappedPublisher
这两个发布者都发布了更新 - 我已经确认了这一点。但是,组合发布者 coordinatePublisher
仅在点击 "center map on current location" 按钮时触发。当 initialUserCoordinate
属性 首次设置时它永远不会触发。
Publishers.CombineLatest($initialUserCoordinate, centerButtonTappedPublisher)
之后添加一个 .receive(on: RunLoop.main)
但这对我不起作用。
我做错了什么?
需要使用Publishers.Merge
rather than CombineLatest
,查看文档:
对于Publishers.CombineLatest
:
A publisher that receives and combines the latest elements from two publishers.
对于Publishers.Merge
A publisher that emits an event when either upstream publisher emits an event.