diff --git a/Sources/ExtensionKit/CoreLocation/CLLocationManager.swift b/Sources/ExtensionKit/CoreLocation/CLLocationManager.swift index 5969ae5..fd1a230 100644 --- a/Sources/ExtensionKit/CoreLocation/CLLocationManager.swift +++ b/Sources/ExtensionKit/CoreLocation/CLLocationManager.swift @@ -28,6 +28,16 @@ public extension CLLocationManager { AuthorizationPublisher(manager: manager, authorizationType: type) .eraseToAnyPublisher() } + + /// Receive location updates from the `CLLocationManager` + /// - Parameter manager: `CLLocationManager` + /// - Returns: Publisher with `[CLLocation]` or `Error` + static func receiveLocationUpdates( + from manager: CLLocationManager = .init() + ) -> AnyPublisher<[CLLocation], Error> { + LocationPublisher(manager: manager) + .eraseToAnyPublisher() + } /// Authorization access level enum AuthorizationType: String { @@ -35,89 +45,3 @@ public extension CLLocationManager { } } - -protocol PublisherAuthorizationDelegate: class { - func send(status: CLAuthorizationStatus) -} - -protocol SubscriptionAuthorizationDelegate: class { - func requestAuthorization(type: CLLocationManager.AuthorizationType) -} - -final class AuthorizationSubscription : NSObject, - PublisherAuthorizationDelegate, - Subscription where S.Input == CLAuthorizationStatus, - S.Failure == Never { - - typealias Output = CLAuthorizationStatus - typealias Failure = Never - - var subscriber: S? - private weak var delegate: SubscriptionAuthorizationDelegate? - private let authorizationType: CLLocationManager.AuthorizationType - - init( - subscriber: S, - authorizationType: CLLocationManager.AuthorizationType, - delegate: SubscriptionAuthorizationDelegate - ) { - self.subscriber = subscriber - self.delegate = delegate - self.authorizationType = authorizationType - } - - func request(_ demand: Subscribers.Demand) { - delegate?.requestAuthorization(type: authorizationType) - } - - func cancel() { - subscriber = nil - delegate = nil - } - - func send(status: CLAuthorizationStatus) { - _ = subscriber?.receive(status) - } -} - -final class AuthorizationPublisher: NSObject, Publisher, CLLocationManagerDelegate, SubscriptionAuthorizationDelegate { - - typealias Output = CLAuthorizationStatus - typealias Failure = Never - - private let manager: CLLocationManager - private let authorizationType: CLLocationManager.AuthorizationType - private weak var publisherAuthorizationDelegate: PublisherAuthorizationDelegate? - - init(manager: CLLocationManager, authorizationType: CLLocationManager.AuthorizationType) { - self.manager = manager - self.authorizationType = authorizationType - } - - func receive(subscriber: S) where S: Subscriber, AuthorizationPublisher.Failure == S.Failure, AuthorizationPublisher.Output == S.Input { - let subscription = AuthorizationSubscription( - subscriber: subscriber, - authorizationType: authorizationType, - delegate: self - ) - subscriber.receive(subscription: subscription) - publisherAuthorizationDelegate = subscription - } - - // MARK: - CLLocationManagerDelegate - - func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { - publisherAuthorizationDelegate?.send(status: status) - } - - // MARK: - AuthorizationSubscriptionDelegate - - func requestAuthorization(type: CLLocationManager.AuthorizationType) { - switch type { - case .whenInUse: - manager.requestWhenInUseAuthorization() - case .always: - manager.requestAlwaysAuthorization() - } - } -} diff --git a/Sources/ExtensionKit/CoreLocation/LocationPublisher.swift b/Sources/ExtensionKit/CoreLocation/LocationPublisher.swift new file mode 100644 index 0000000..ab24f7d --- /dev/null +++ b/Sources/ExtensionKit/CoreLocation/LocationPublisher.swift @@ -0,0 +1,91 @@ +import Combine +import CoreLocation + +protocol PublisherLocationDelegate: class { + func startLocationUpdates() +} + +protocol SubscriptionLocationDelegate: class { + func didUpdate(with locations: [CLLocation]) + func didFail(with error: Error) +} + +final class LocationSubscription : + NSObject, + SubscriptionLocationDelegate, + Subscription where S.Input == [CLLocation], + S.Failure == Error { + + var subscriber: S? + private weak var publisherDelegate: PublisherLocationDelegate? + + init(subscriber: S?, delegate: PublisherLocationDelegate?) { + self.subscriber = subscriber + self.publisherDelegate = delegate + } + + func didUpdate(with locations: [CLLocation]) { + _ = subscriber?.receive(locations) + } + + func didFail(with error: Error) { + _ = subscriber?.receive(completion: .failure(error)) + } + + func request(_ demand: Subscribers.Demand) { + publisherDelegate?.startLocationUpdates() + } + + func cancel() { + subscriber = nil + publisherDelegate = nil + } + +} + +final class LocationPublisher: NSObject, + Publisher, + CLLocationManagerDelegate, + PublisherLocationDelegate { + typealias Output = [CLLocation] + typealias Failure = Error + + private let manager: CLLocationManager + private weak var subscriberDelegate: SubscriptionLocationDelegate? + + init(manager: CLLocationManager) { + self.manager = manager + super.init() + self.manager.delegate = self + } + + // MARK: Publisher + + func receive(subscriber: S) where S : Subscriber, LocationPublisher.Failure == S.Failure, LocationPublisher.Output == S.Input { + let subscibtion = LocationSubscription( + subscriber: subscriber, + delegate: self + ) + subscriber.receive(subscription: subscibtion) + subscriberDelegate = subscibtion + } + + // MARK: CLLocationManagerDelegate + + func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { + subscriberDelegate?.didFail(with: error) + manager.stopUpdatingLocation() + } + + func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { + subscriberDelegate?.didUpdate(with: locations) + } + + // MARK: PublisherLocationDelegate + + func startLocationUpdates() { + manager.startUpdatingLocation() + } +} + +