Skip to content

Instantly share code, notes, and snippets.

@danielt1263
Last active March 17, 2023 02:29
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danielt1263/64bda2a32c18b8c28e1e22085a05df5a to your computer and use it in GitHub Desktop.
Save danielt1263/64bda2a32c18b8c28e1e22085a05df5a to your computer and use it in GitHub Desktop.
//
// CLGeocoder+Rx.swift
//
// Created by Daniel Tartaglia on 5/7/16.
// Copyright © 2018 Daniel Tartaglia. MIT License.
//
import RxSwift
import CoreLocation
import Contacts
public extension Reactive where Base: CLGeocoder {
func reverseGeocodeLocation(_ location: CLLocation) -> Observable<[CLPlacemark]> {
return Observable<[CLPlacemark]>.create { observer in
semaphore.wait()
geocodeHandler(observer: observer, geocode: curry2(self.base.reverseGeocodeLocation(_: completionHandler:), location))
return Disposables.create(with: cancel(self.base))
}
.subscribe(on: scheduler)
}
@available(iOS, introduced: 5.0, deprecated: 11.0, message: "Use -geocodePostalAddress:")
func geocodeAddressDictionary(_ addressDictionary: [NSObject : AnyObject]) -> Observable<[CLPlacemark]> {
return Observable<[CLPlacemark]>.create { observer in
semaphore.wait()
geocodeHandler(observer: observer, geocode: curry2(self.base.geocodeAddressDictionary(_: completionHandler:), addressDictionary))
return Disposables.create(with: cancel(self.base))
}
.subscribe(on: scheduler)
}
@available(iOS 11.0, *)
func geocodePostalAddress(_ postalAddress: CNPostalAddress) -> Observable<[CLPlacemark]> {
return Observable<[CLPlacemark]>.create { observer in
semaphore.wait()
geocodeHandler(observer: observer, geocode: curry2(self.base.geocodePostalAddress(_: completionHandler:), postalAddress))
return Disposables.create(with: cancel(self.base))
}
.subscribe(on: scheduler)
}
func geocodeAddressString(_ addressString: String) -> Observable<[CLPlacemark]> {
return Observable<[CLPlacemark]>.create { observer in
semaphore.wait()
geocodeHandler(observer: observer, geocode: curry2(self.base.geocodeAddressString(_: completionHandler:), addressString))
return Disposables.create(with: cancel(self.base))
}
.subscribe(on: scheduler)
}
func geocodeAddressString(_ addressString: String, in region: CLRegion?) -> Observable<[CLPlacemark]> {
return Observable<[CLPlacemark]>.create { observer in
semaphore.wait()
geocodeHandler(observer: observer, geocode: curry3(self.base.geocodeAddressString(_: in: completionHandler:), addressString, region))
return Disposables.create(with: cancel(self.base))
}
.subscribe(on: scheduler)
}
}
private let semaphore = DispatchSemaphore(value: 1)
private let scheduler = SerialDispatchQueueScheduler(internalSerialQueueName: "CLGeocoderRx")
private func curry2<A, B, C>(_ f: @escaping (A, B) -> C, _ a: A) -> (B) -> C {
return { b in f(a, b) }
}
private func curry3<A, B, C, D>(_ f: @escaping (A, B, C) -> D, _ a: A, _ b: B) -> (C) -> D {
return { c in f(a, b, c) }
}
private func geocodeHandler(observer: AnyObserver<[CLPlacemark]>, geocode: @escaping (@escaping CLGeocodeCompletionHandler) -> Void) {
geocode { placemarks, error in
if let placemarks = placemarks {
observer.onNext(placemarks)
observer.onCompleted()
}
else if let error = error {
observer.onError(error)
}
else {
observer.onError(RxError.unknown)
}
}
}
private func cancel(_ geocoder: CLGeocoder) -> () -> Void {
return {
geocoder.cancelGeocode()
semaphore.signal()
}
}
@HassanSE
Copy link

Getting a warning:
geocodeAddressDictionary(_:completionHandler:)' was deprecated in iOS 11.0: Use -geocodePostalAddress:completionHandler:

@danielt1263
Copy link
Author

@HassanSE I fixed the warning.

@danielt1263
Copy link
Author

danielt1263 commented Sep 28, 2018

The documentation for CLGeocoder says that you can't execute a geocode request while one is outstanding. So I have implemented a semaphore to ensure that calls happen sequentially.

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