Skip to content

Commit

Permalink
Merge pull request #96 from UbiqueInnovation/feature/location-authori…
Browse files Browse the repository at this point in the history
…zation

Feature/location authorization
  • Loading branch information
ubfelix authored Oct 4, 2024
2 parents 3b11878 + bc4668f commit 860ae44
Show file tree
Hide file tree
Showing 3 changed files with 16 additions and 55 deletions.
55 changes: 8 additions & 47 deletions Sources/UBLocation/UBLocationManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,24 +104,7 @@ public class UBLocationManager: NSObject {

/// Allows logging all the changes in authorization status, separately from any delegates
public var logLocationPermissionChange: ((CLAuthorizationStatus) -> Void)?
private var authorizationStatus: CLAuthorizationStatus? {
didSet {
if let authorizationStatus {
self.authorizationCallbackTimers.values.forEach {
$0.invalidate()
}
self.authorizationCallbackTimers.removeAll()
self.pendingAuthorizationStatusCallbacks.values.forEach {
$0(authorizationStatus)
}
self.pendingAuthorizationStatusCallbacks.removeAll()
}
}
}

private var pendingAuthorizationStatusCallbacks: [UUID: (CLAuthorizationStatus) -> Void] = [:]
private var authorizationCallbackTimers: [UUID: Timer] = [:]
private let timeoutTime: TimeInterval = 4
public private(set) var authorizationStatus: CLAuthorizationStatus

/// The desired location accuracy of the underlying `CLLocationManager`
public var desiredAccuracy: CLLocationAccuracy {
Expand Down Expand Up @@ -207,8 +190,7 @@ public class UBLocationManager: NSObject {
private var lastDelegateFreshState: [ObjectIdentifier: Bool] = [:]

/// Does the location manager have the required authorization level for `usage`?
public static func hasRequiredAuthorizationLevel(forUsage usage: Set<LocationMonitoringUsage>) -> Bool {
let authorizationStatus = CLLocationManager.authorizationStatus()
public func hasRequiredAuthorizationLevel(forUsage usage: Set<LocationMonitoringUsage>) -> Bool {
switch authorizationStatus {
case .authorizedAlways:
return true
Expand All @@ -229,30 +211,10 @@ public class UBLocationManager: NSObject {
}

/// Does the location manager have the required authorization level for `usage`?
public static func hasRequiredAuthorizationLevel(forUsage usage: LocationMonitoringUsage) -> Bool {
public func hasRequiredAuthorizationLevel(forUsage usage: LocationMonitoringUsage) -> Bool {
hasRequiredAuthorizationLevel(forUsage: Set([usage]))
}

public func getAuthorizationStatus(completion: @escaping (CLAuthorizationStatus) -> Void) {
if let authorizationStatus {
completion(authorizationStatus)
} else {
let id = UUID()
let timer = Timer.scheduledTimer(withTimeInterval: timeoutTime, repeats: false) { [weak self] _ in
guard let self else { return }
MainActor.assumeIsolated {
if let callback = self.pendingAuthorizationStatusCallbacks[id] {
callback(.notDetermined)
self.pendingAuthorizationStatusCallbacks.removeValue(forKey: id)
}
self.authorizationCallbackTimers.removeValue(forKey: id)
}
}
authorizationCallbackTimers[id] = timer
pendingAuthorizationStatusCallbacks[id] = completion
}
}

/// The underlying location manager
private(set) var locationManager: UBLocationManagerProtocol

Expand All @@ -270,6 +232,7 @@ public class UBLocationManager: NSObject {
/// - locationManager: The underlying location manager
init(locationManager: UBLocationManagerProtocol = CLLocationManager()) {
self.locationManager = locationManager
authorizationStatus = locationManager.authorizationStatus
timeout = Self.defaultTimeout

super.init()
Expand Down Expand Up @@ -324,9 +287,8 @@ public class UBLocationManager: NSObject {
let id = ObjectIdentifier(delegate)
delegateWrappers[id] = wrapper

let authStatus = locationManager.authorizationStatus()
let minimumAuthorizationLevelRequired = usage.minimumAuthorizationLevelRequired
switch authStatus {
switch authorizationStatus {
case .authorizedAlways:
startLocationMonitoringWithoutChecks(delegate, usage: usage)
case .authorizedWhenInUse:
Expand Down Expand Up @@ -442,10 +404,9 @@ public class UBLocationManager: NSObject {
self.permissionRequestUsage = nil
}

let authStatus = locationManager.authorizationStatus()
let minimumAuthorizationLevelRequired = usage.minimumAuthorizationLevelRequired

switch authStatus {
switch authorizationStatus {
case .authorizedAlways:
callback(.success)

Expand Down Expand Up @@ -574,7 +535,7 @@ extension UBLocationManager: @preconcurrency CLLocationManagerDelegate {

self.startLocationMonitoringForAllDelegates()

if Self.hasRequiredAuthorizationLevel(forUsage: allUsages) {
if hasRequiredAuthorizationLevel(forUsage: allUsages) {
let permission: AuthorizationLevel = authorization == .authorizedAlways ? .always : .whenInUse

for delegate in delegates() {
Expand All @@ -590,7 +551,7 @@ extension UBLocationManager: @preconcurrency CLLocationManagerDelegate {
// permission request callbacks
if let usage = self.permissionRequestUsage,
let callback = self.permissionRequestCallback {
let hasRequiredLevel = Self.hasRequiredAuthorizationLevel(forUsage: usage)
let hasRequiredLevel = hasRequiredAuthorizationLevel(forUsage: usage)
callback(hasRequiredLevel ? .success : .failure)

self.permissionRequestCallback = nil
Expand Down
6 changes: 1 addition & 5 deletions Sources/UBLocation/UBLocationManagerProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,13 @@ public protocol UBLocationManagerProtocol {
func requestWhenInUseAuthorization()
func requestAlwaysAuthorization()

func authorizationStatus() -> CLAuthorizationStatus
var authorizationStatus: CLAuthorizationStatus { get }
func locationServicesEnabled() -> Bool
func significantLocationChangeMonitoringAvailable() -> Bool
func isMonitoringAvailable(for regionClass: AnyClass) -> Bool
}

extension CLLocationManager: UBLocationManagerProtocol {
public func authorizationStatus() -> CLAuthorizationStatus {
CLLocationManager.authorizationStatus()
}

@available(*, deprecated, message: "locationServicesEnabled() not exposed to avoid use on main thread. Use CLLocationManager.locationServicesEnabled() directly if needed.")
public func locationServicesEnabled() -> Bool {
CLLocationManager.locationServicesEnabled()
Expand Down
10 changes: 7 additions & 3 deletions Tests/UBLocationTests/MockLocationManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ import UBLocation

class MockLocationManager: UBLocationManagerProtocol {
/// The sequence of authorizationStatues that is traversed when requesting Authorization
var authorizationStatuses: [CLAuthorizationStatus] = []
var authorizationStatuses: [CLAuthorizationStatus] = [] {
didSet {
delegate?.locationManager?(CLLocationManager(), didChangeAuthorization: _authorizationStatus)
}
}

var _authorizationStatus: CLAuthorizationStatus {
guard let currentAuthorizationStatus = authorizationStatuses.first else {
fatalError()
return .notDetermined
}
return currentAuthorizationStatus
}
Expand Down Expand Up @@ -100,7 +104,7 @@ class MockLocationManager: UBLocationManagerProtocol {
delegate?.locationManager?(CLLocationManager(), didChangeAuthorization: _authorizationStatus)
}

func authorizationStatus() -> CLAuthorizationStatus {
var authorizationStatus: CLAuthorizationStatus {
_authorizationStatus
}

Expand Down

0 comments on commit 860ae44

Please sign in to comment.