Skip to content

Instantly share code, notes, and snippets.

@zarghol
Created November 25, 2019 12:30
Show Gist options
  • Save zarghol/a44353833b508abb7b9046151fdf086b to your computer and use it in GitHub Desktop.
Save zarghol/a44353833b508abb7b9046151fdf086b to your computer and use it in GitHub Desktop.
Combine+Pattern Delegate
import Foundation
import Combine
import CoreLocation
extension Subscriber {
func eraseToAnySubscriber() -> AnySubscriber<Self.Input, Self.Failure> {
return AnySubscriber<Input, Failure>.init(receiveSubscription: { sub in
self.receive(subscription: sub)
}, receiveValue: { value in
self.receive(value)
}, receiveCompletion: { completion in
self.receive(completion: completion)
})
}
}
final class CLLocationManagerPublisher: NSObject, CLLocationManagerDelegate, Publisher {
typealias Output = Data
typealias Failure = Error
private weak var manager: CLLocationManager?
private var subscriptions: [CLLocationManagerSubscription]
init(locationManager: CLLocationManager) {
self.manager = locationManager
self.subscriptions = []
super.init()
locationManager.delegate = self
}
func receive<S>(subscriber: S) where S : Subscriber, CLLocationManagerPublisher.Failure == S.Failure, CLLocationManagerPublisher.Output == S.Input {
let subscription = CLLocationManagerSubscription(publisher: self, subscriber: subscriber)
self.subscriptions.append(subscription)
subscriber.receive(subscription: subscription)
}
private func remove(subscription: CLLocationManagerSubscription) {
self.subscriptions.removeAll(where: { $0 === subscription })
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
self.subscriptions.forEach {
$0.handleValue(.authorizationChange(status))
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
self.subscriptions.forEach {
$0.handleValue(.locations(locations))
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
self.subscriptions.forEach {
$0.handleValue(.error(error))
}
}
var errors: AnyPublisher<Error, Never> {
return self.compactMap({ data in
switch data {
case .error(let error):
return error
default:
return nil
}
})
.assertNoFailure()
.eraseToAnyPublisher()
}
var locations: AnyPublisher<[CLLocation], Never> {
return self.compactMap({ data in
switch data {
case .locations(let locations):
return locations
default:
return nil
}
})
.assertNoFailure()
.eraseToAnyPublisher()
}
var status: AnyPublisher<CLAuthorizationStatus, Never> {
return self.compactMap({ data in
switch data {
case .authorizationChange(let status):
return status
default:
return nil
}
})
.assertNoFailure()
.eraseToAnyPublisher()
}
}
extension CLLocationManagerPublisher {
final class CLLocationManagerSubscription: Subscription {
let subscriber: AnySubscriber<Data, Error>
weak var publisher: CLLocationManagerPublisher?
var currentDemand: Subscribers.Demand = .unlimited
init<S: Subscriber>(publisher: CLLocationManagerPublisher, subscriber: S) where S.Failure == Error, S.Input == Data {
self.subscriber = subscriber.eraseToAnySubscriber()
self.publisher = publisher
}
func request(_ demand: Subscribers.Demand) {
currentDemand = demand
}
func handleValue(_ value: Data) {
let additionalDemand = subscriber.receive(value)
currentDemand += additionalDemand
if currentDemand <= 0 {
subscriber.receive(completion: .finished)
publisher?.remove(subscription: self)
}
}
func cancel() {
publisher?.remove(subscription: self)
}
}
}
extension CLLocationManagerPublisher {
enum Data {
case locations([CLLocation])
case authorizationChange(CLAuthorizationStatus)
case error(Error)
}
}
@KarenK1995
Copy link

Can you also provide a usage example? I tried to use this publisher but seems no values are delivered.

@malhal
Copy link

malhal commented Sep 24, 2020

Can you also provide a usage example? I tried to use this publisher but seems no values are delivered.

Please try my attempt: https://gist.github.com/malhal/22f534d47d620216c25d812af9bcc227

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment