Skip to content

Instantly share code, notes, and snippets.

@moaible
Last active December 16, 2017 16:33
Show Gist options
  • Select an option

  • Save moaible/d857d2b522e7ad9a3941fee7bc45a9cc to your computer and use it in GitHub Desktop.

Select an option

Save moaible/d857d2b522e7ad9a3941fee7bc45a9cc to your computer and use it in GitHub Desktop.
Geofence.swift
import CoreLocation
import CoreMotion
struct Geofence {
typealias Degreese = Double
typealias Radius = Double
var identifier: String
var radius: Radius
var latitude: Degreese
var longitude: Degreese
}
extension Geofence {
enum PermissionMode {
case always
case whenInAppUse
}
enum Error: Swift.Error {
case permissionDenied
case permissionRestricted
case permissionNotDetermined
}
}
protocol GeofenceBuildable {
func ensurePermission() throws -> Geofence.PermissionMode
func requestPermission(
mode: Geofence.PermissionMode,
completion: @escaping ((Geofence.PermissionMode?, Geofence.Error?) -> Void))
func build(geofence: Geofence)
func destroy(geofenceIdentifier: String)
func destroyAll()
}
class GeofenceHandler: NSObject {
let manager = CLLocationManager()
var enteringGeofence: ((String) -> Void)?
var exitingGeofence: ((String?, Error?) -> Void)?
override init() {
super.init()
manager.delegate = self
}
func ensurePermission() throws -> Geofence.PermissionMode {
let authorizationStatus = CLLocationManager.authorizationStatus()
switch authorizationStatus {
case .denied:
throw Geofence.Error.permissionDenied
case .restricted:
throw Geofence.Error.permissionRestricted
case .notDetermined:
throw Geofence.Error.permissionNotDetermined
case .authorizedAlways:
return .always
case .authorizedWhenInUse:
return .whenInAppUse
}
}
private var requestPermissionHandler: ((Geofence.PermissionMode?, Geofence.Error?) -> Void)?
func requestPermission(
mode: Geofence.PermissionMode,
completion: @escaping ((Geofence.PermissionMode?, Geofence.Error?) -> Void) = { _, _ in })
{
requestPermissionHandler = { mode, error in
completion(mode, error)
}
switch mode {
case .always:
manager.requestAlwaysAuthorization()
case .whenInAppUse:
manager.requestWhenInUseAuthorization()
}
}
func addGeofence(geofence: Geofence) {
let center = CLLocationCoordinate2DMake(geofence.latitude, geofence.longitude)
let region = CLCircularRegion(center: center, radius: geofence.radius, identifier: geofence.identifier)
manager.startMonitoring(for: region)
}
func removeGeofence(geofenceIdentifier: String) {
guard let region = manager.monitoredRegions.first(where: { region in
region.identifier == geofenceIdentifier
}) else {
return
}
manager.stopMonitoring(for: region)
}
func removeAllGeofence() {
manager.monitoredRegions.forEach { [weak self] region in
self?.manager.stopMonitoring(for: region)
}
}
}
extension GeofenceHandler: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
guard status != .notDetermined else {
return
}
do {
let mode = try ensurePermission()
requestPermissionHandler?(mode, nil)
} catch {
precondition(error is Geofence.Error)
requestPermissionHandler?(nil, error as? Geofence.Error)
}
}
func locationManager(_ manager: CLLocationManager, didStartMonitoringFor region: CLRegion) {
enteringGeofence?(region.identifier)
}
func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error) {
exitingGeofence?(region?.identifier, error)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment