Skip to content

Commit

Permalink
feat: add publisher for location updates
Browse files Browse the repository at this point in the history
  • Loading branch information
gtokman committed May 5, 2021
1 parent bec8289 commit 91f38ce
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 86 deletions.
96 changes: 10 additions & 86 deletions Sources/ExtensionKit/CoreLocation/CLLocationManager.swift
Expand Up @@ -28,96 +28,20 @@ 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 {
case always, whenInUse
}

}

protocol PublisherAuthorizationDelegate: class {
func send(status: CLAuthorizationStatus)
}

protocol SubscriptionAuthorizationDelegate: class {
func requestAuthorization(type: CLLocationManager.AuthorizationType)
}

final class AuthorizationSubscription <S: Subscriber>: 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<S>(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()
}
}
}
91 changes: 91 additions & 0 deletions 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 <S: Subscriber>:
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<S>(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()
}
}


0 comments on commit 91f38ce

Please sign in to comment.