mirror of
https://github.com/samsonjs/Advanced-NSOperations.git
synced 2026-03-25 08:25:47 +00:00
144 lines
4.8 KiB
Swift
144 lines
4.8 KiB
Swift
/*
|
||
Copyright (C) 2015 Apple Inc. All Rights Reserved.
|
||
See LICENSE.txt for this sample’s licensing information
|
||
|
||
Abstract:
|
||
This file shows an example of implementing the OperationCondition protocol.
|
||
*/
|
||
|
||
import CoreLocation
|
||
|
||
/// A condition for verifying access to the user's location.
|
||
struct LocationCondition: OperationCondition {
|
||
/**
|
||
Declare a new enum instead of using `CLAuthorizationStatus`, because that
|
||
enum has more case values than are necessary for our purposes.
|
||
*/
|
||
enum Usage {
|
||
case WhenInUse
|
||
case Always
|
||
}
|
||
|
||
static let name = "Location"
|
||
static let locationServicesEnabledKey = "CLLocationServicesEnabled"
|
||
static let authorizationStatusKey = "CLAuthorizationStatus"
|
||
static let isMutuallyExclusive = false
|
||
|
||
let usage: Usage
|
||
|
||
init(usage: Usage) {
|
||
self.usage = usage
|
||
}
|
||
|
||
func dependencyForOperation(operation: EarthquakeOperation) -> Operation? {
|
||
return LocationPermissionOperation(usage: usage)
|
||
}
|
||
|
||
func evaluateForOperation(operation: EarthquakeOperation, completion: (OperationConditionResult) -> Void) {
|
||
let enabled = CLLocationManager.locationServicesEnabled()
|
||
let actual = CLLocationManager().authorizationStatus
|
||
|
||
var error: NSError?
|
||
|
||
// There are several factors to consider when evaluating this condition
|
||
switch (enabled, usage, actual) {
|
||
case (true, _, .authorizedAlways):
|
||
// The service is enabled, and we have "Always" permission -> condition satisfied.
|
||
break
|
||
|
||
case (true, .WhenInUse, .authorizedWhenInUse):
|
||
/*
|
||
The service is enabled, and we have and need "WhenInUse"
|
||
permission -> condition satisfied.
|
||
*/
|
||
break
|
||
|
||
default:
|
||
/*
|
||
Anything else is an error. Maybe location services are disabled,
|
||
or maybe we need "Always" permission but only have "WhenInUse",
|
||
or maybe access has been restricted or denied,
|
||
or maybe access hasn't been request yet.
|
||
|
||
The last case would happen if this condition were wrapped in a `SilentCondition`.
|
||
*/
|
||
error = NSError(code: .ConditionFailed, userInfo: [
|
||
OperationConditionKey: type(of: self).name,
|
||
type(of: self).locationServicesEnabledKey: enabled,
|
||
type(of: self).authorizationStatusKey: Int(actual.rawValue)
|
||
])
|
||
}
|
||
|
||
if let error = error {
|
||
completion(.Failed(error))
|
||
}
|
||
else {
|
||
completion(.Satisfied)
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
A private `EarthquakeOperation` that will request permission to access the user's location,
|
||
if permission has not already been granted.
|
||
*/
|
||
private class LocationPermissionOperation: EarthquakeOperation {
|
||
let usage: LocationCondition.Usage
|
||
var manager: CLLocationManager?
|
||
|
||
init(usage: LocationCondition.Usage) {
|
||
self.usage = usage
|
||
super.init()
|
||
/*
|
||
This is an operation that potentially presents an alert so it should
|
||
be mutually exclusive with anything else that presents an alert.
|
||
*/
|
||
addCondition(condition: AlertPresentation())
|
||
}
|
||
|
||
override func execute() {
|
||
/*
|
||
Not only do we need to handle the "Not Determined" case, but we also
|
||
need to handle the "upgrade" (.WhenInUse -> .Always) case.
|
||
*/
|
||
switch (CLLocationManager().authorizationStatus, usage) {
|
||
case (.notDetermined, _), (.authorizedWhenInUse, .Always):
|
||
DispatchQueue.main.async {
|
||
self.requestPermission()
|
||
}
|
||
|
||
default:
|
||
finish()
|
||
}
|
||
}
|
||
|
||
private func requestPermission() {
|
||
manager = CLLocationManager()
|
||
manager?.delegate = self
|
||
|
||
let key: String
|
||
|
||
switch usage {
|
||
case .WhenInUse:
|
||
key = "NSLocationWhenInUseUsageDescription"
|
||
manager?.requestWhenInUseAuthorization()
|
||
|
||
case .Always:
|
||
key = "NSLocationAlwaysUsageDescription"
|
||
manager?.requestAlwaysAuthorization()
|
||
}
|
||
|
||
// This is helpful when developing the app.
|
||
assert(Bundle.main.object(forInfoDictionaryKey: key) != nil, "Requesting location permission requires the \(key) key in your Info.plist")
|
||
}
|
||
|
||
}
|
||
|
||
extension LocationPermissionOperation: CLLocationManagerDelegate {
|
||
@available(iOS 14.0, *)
|
||
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
|
||
if manager == self.manager && isExecuting && manager.authorizationStatus != .notDetermined {
|
||
finish()
|
||
}
|
||
}
|
||
}
|