diff --git a/Sources/ExtensionKit/CoreLocation/CLLocationManager.swift b/Sources/ExtensionKit/CoreLocation/CLLocationManager.swift new file mode 100644 index 0000000..1a7db0c --- /dev/null +++ b/Sources/ExtensionKit/CoreLocation/CLLocationManager.swift @@ -0,0 +1,104 @@ +import Combine +import CoreLocation + +public extension CLLocationManager { + + static func requestLocationAuthorization( + with manager: CLLocationManager = .init(), + type: AuthorizationType + ) -> AnyPublisher { + AuthorizationPublisher(manager: manager, authorizationType: type) + .eraseToAnyPublisher() + } + + enum AuthorizationType: String { + case always, whenInUse + } + +} + +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 + ) + 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() + } + } +}