Last active
May 31, 2024 23:28
-
-
Save malhal/22f534d47d620216c25d812af9bcc227 to your computer and use it in GitHub Desktop.
A Combine location publisher for CLLocationManager.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Requirements: a NSLocationWhenInUseUsageDescription entry in Info.plist | |
// Usage: @State var locator = CLLocationManager.publishLocation() | |
// and | |
// .onReceive(locator) { location in | |
// Improvements needed: Move requestWhenInUseAuthorization into its own publisher and perhaps have a combineLatest pipeline for both authorized and valid location. | |
// A configuration param to init(), e.g. so each manager can have the same distanceFilter. | |
import Foundation | |
import Combine | |
import CoreLocation | |
extension CLLocationManager { | |
public static func publishLocation() -> LocationPublisher{ | |
return .init() | |
} | |
public struct LocationPublisher: Publisher { | |
public typealias Output = CLLocation | |
public typealias Failure = Never | |
public func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input { | |
let subscription = LocationSubscription(subscriber: subscriber) | |
subscriber.receive(subscription: subscription) | |
} | |
final class LocationSubscription<S: Subscriber> : NSObject, CLLocationManagerDelegate, Subscription where S.Input == Output, S.Failure == Failure{ | |
var subscriber: S | |
var locationManager = CLLocationManager() | |
init(subscriber: S) { | |
self.subscriber = subscriber | |
super.init() | |
locationManager.delegate = self | |
} | |
func request(_ demand: Subscribers.Demand) { | |
locationManager.startUpdatingLocation() | |
locationManager.requestWhenInUseAuthorization() | |
} | |
func cancel() { | |
locationManager.stopUpdatingLocation() | |
} | |
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { | |
for location in locations { | |
_ = subscriber.receive(location) | |
} | |
} | |
} | |
} | |
} | |
// Tester | |
// | |
//import SwiftUI | |
//import CoreData | |
//import CoreLocation | |
//import Combine | |
// | |
//struct ContentView: View { | |
// | |
// @State private var counter = 0 | |
// var body: some View { | |
// VStack { | |
// ContentView2() | |
// Button("Malc \(counter)") { | |
// counter += 1 | |
// } | |
// } | |
// | |
// } | |
//} | |
// | |
//struct ContentView2: View { | |
// | |
// @State var timer = Timer.publish(every: 1, on: .main, in:.common).autoconnect() | |
// @State var locator = CLLocationManager.publishLocation() | |
// @State private var counter = 0 | |
// @State var locationString = "" | |
// var body: some View { | |
// VStack { | |
// Text("Hello, World! \(counter)") | |
// Text("Location \(locationString)") | |
// } | |
// .onReceive(timer) { time in | |
//// if self.counter == 5 { | |
//// self.timer.upstream.connect().cancel() | |
//// } else { | |
//// // print("The time is now \(time)") | |
//// } | |
// self.counter += 1 | |
// } | |
// .onReceive(locator) { location in | |
// locationString = String(format: "%.5f,%.5f %@", location.coordinate.latitude, location.coordinate.longitude, location.timestamp.description) | |
// } | |
// } | |
//} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For my current purpose it's enough and a great starting point in any case. Thanks a lot!