I have only started learning combine, so it's still a little fuzzy to me. I would like to create a custom Publisher
, that would use CLLocationManager
to expose current user location. I would like it to work in such a way, that locationManager
only starts updating location when there are some subscribers connected. And after all subscribers were removed, canceled, etc, then it should stop updating location. Is this possible to do? How can I create such a publisher
? Also is this the right approach, or is there something wrong with it?
The basics of what you want are pretty straightforward. There's an example from Using Combine that wraps CoreLocation with a object that acts as a proxy, returning a publisher of CLHeading
updates.
CoreLocation itself doesn't automatically start or stop that sort of thing, and in the proxy object I copied that pattern to allow you to manually start and stop the updating process.
The core of the code is in https://github.com/heckj/swiftui-notes/blob/master/UIKit-Combine/LocationHeadingProxy.swift
import Foundation
import Combine
import CoreLocation
final class LocationHeadingProxy: NSObject, CLLocationManagerDelegate {
let mgr: CLLocationManager
private let headingPublisher: PassthroughSubject<CLHeading, Error>
var publisher: AnyPublisher<CLHeading, Error>
override init() {
mgr = CLLocationManager()
headingPublisher = PassthroughSubject<CLHeading, Error>()
publisher = headingPublisher.eraseToAnyPublisher()
super.init()
mgr.delegate = self
}
func enable() {
mgr.startUpdatingHeading()
}
func disable() {
mgr.stopUpdatingHeading()
}
// MARK - delegate methods
func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
headingPublisher.send(newHeading)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
headingPublisher.send(completion: Subscribers.Completion.failure(error))
}
}
This doesn't quite do everything you asked, in that it doesn't automatically start updates upon subscription, but I suspect you could expand this to enable that capability.
So far I haven't delved into making my own publishers by implementing all the methods required by the protocol, so I don't have detail extending to that mechanism. Combine itself has the concept of ConnectablePublisher for when you want explicit control over the updates, although most of the publishers and operators trigger on either creation of the publisher or subscription.
In general, IF you should do so is better answered by your use case. In some instances, you're creating the pipelines and subscribing before you're updating views - if that's the case, then holding off on asking for the background updates would save you some processing power and energy consumption.
In the UIKit examples that use this CoreLocation publisher, I also have the mechanisms in place to verify permissions have been requested to allow for Location updates, embedded within an example view controller: https://github.com/heckj/swiftui-notes/blob/master/UIKit-Combine/HeadingViewController.swift
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With