Error handling robustness
- Don't fetch if we know we don't have access - Allow fetching current location in parallel Also: Comments and README info
This commit is contained in:
parent
7ccc0c911c
commit
bf411c9c40
|
@ -11,7 +11,10 @@ Relies on a mixture of techniques, such as:
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
TODO:
|
1. Configure your *Target*:
|
||||||
|
- Go to *Signing & Capabilities*, *Background Modes* and make sure *Location updates* is ticket.
|
||||||
|
- Go to *Info*, and make sure you have usage descriptions for "Privacy - Location Always", "Privacy - Location Always and When in Use", and "Privacy - Location When In Use" set.
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,8 @@ public class GeoMonitor: NSObject, ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum LocationFetchError: Error {
|
public enum LocationFetchError: Error {
|
||||||
|
case accessNotProvided
|
||||||
|
|
||||||
/// Happens if you stop monitoring before a location could be found
|
/// Happens if you stop monitoring before a location could be found
|
||||||
case noLocationFetchedInTime
|
case noLocationFetchedInTime
|
||||||
|
|
||||||
|
@ -59,6 +61,10 @@ public class GeoMonitor: NSObject, ObservableObject {
|
||||||
|
|
||||||
public var maxRegionsToMonitor = 20
|
public var maxRegionsToMonitor = 20
|
||||||
|
|
||||||
|
/// Instantiates new monitor
|
||||||
|
/// - Parameters:
|
||||||
|
/// - fetch: Handler that's called when the monitor decides it's a good time to update the regions to monitor. Should fetch and then return all regions to be monitored (even if they didn't change).
|
||||||
|
/// - onEvent: Handler that's called when a relevant event is happening, including when one of the monitored regions is entered.
|
||||||
public convenience init(fetch: @escaping (GeoMonitor.FetchTrigger) async -> [CLCircularRegion], onEvent: @escaping (Event) -> Void) {
|
public convenience init(fetch: @escaping (GeoMonitor.FetchTrigger) async -> [CLCircularRegion], onEvent: @escaping (Event) -> Void) {
|
||||||
self.init(dataSource: SimpleDataSource(handler: fetch), onEvent: onEvent)
|
self.init(dataSource: SimpleDataSource(handler: fetch), onEvent: onEvent)
|
||||||
}
|
}
|
||||||
|
@ -156,7 +162,7 @@ public class GeoMonitor: NSObject, ObservableObject {
|
||||||
|
|
||||||
private var isMonitoring: Bool = false
|
private var isMonitoring: Bool = false
|
||||||
|
|
||||||
private var withNextLocation: ((Result<CLLocation, Error>) -> Void)? = nil
|
private var withNextLocation: [(Result<CLLocation, Error>) -> Void] = []
|
||||||
|
|
||||||
private var currentLocationRegion: CLRegion? = nil
|
private var currentLocationRegion: CLRegion? = nil
|
||||||
|
|
||||||
|
@ -173,7 +179,7 @@ public class GeoMonitor: NSObject, ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func startMonitoring() {
|
public func startMonitoring() {
|
||||||
guard !isMonitoring else { return }
|
guard !isMonitoring, hasAccess else { return }
|
||||||
|
|
||||||
isMonitoring = true
|
isMonitoring = true
|
||||||
locationManager.allowsBackgroundLocationUpdates = true
|
locationManager.allowsBackgroundLocationUpdates = true
|
||||||
|
@ -207,21 +213,19 @@ public class GeoMonitor: NSObject, ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func fetchCurrentLocation() async throws -> CLLocation {
|
private func fetchCurrentLocation() async throws -> CLLocation {
|
||||||
if let existing = withNextLocation {
|
guard hasAccess else {
|
||||||
assertionFailure()
|
throw LocationFetchError.accessNotProvided
|
||||||
existing(.failure(LocationFetchError.noLocationFetchedInTime))
|
|
||||||
withNextLocation = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let originalAccuracy = locationManager.desiredAccuracy
|
let originalAccuracy = locationManager.desiredAccuracy
|
||||||
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
|
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
|
||||||
locationManager.requestLocation()
|
locationManager.requestLocation()
|
||||||
|
|
||||||
return try await withCheckedThrowingContinuation { continuation in
|
return try await withCheckedThrowingContinuation { continuation in
|
||||||
withNextLocation = { [unowned self] result in
|
withNextLocation.append({ [unowned self] result in
|
||||||
self.locationManager.desiredAccuracy = originalAccuracy
|
self.locationManager.desiredAccuracy = originalAccuracy
|
||||||
continuation.resume(with: result)
|
continuation.resume(with: result)
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -276,8 +280,10 @@ extension GeoMonitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
func stopMonitoringCurrentArea() {
|
func stopMonitoringCurrentArea() {
|
||||||
withNextLocation?(.failure(LocationFetchError.noLocationFetchedInTime))
|
withNextLocation.forEach {
|
||||||
withNextLocation = nil
|
$0(.failure(LocationFetchError.noLocationFetchedInTime))
|
||||||
|
}
|
||||||
|
withNextLocation = []
|
||||||
currentLocationRegion = nil
|
currentLocationRegion = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -400,15 +406,19 @@ extension GeoMonitor: CLLocationManagerDelegate {
|
||||||
.filter({ $0.horizontalAccuracy <= manager.desiredAccuracy })
|
.filter({ $0.horizontalAccuracy <= manager.desiredAccuracy })
|
||||||
.last
|
.last
|
||||||
else {
|
else {
|
||||||
withNextLocation?(.failure(LocationFetchError.locationInaccurate(latest)))
|
withNextLocation.forEach {
|
||||||
withNextLocation = nil
|
$0(.failure(LocationFetchError.locationInaccurate(latest)))
|
||||||
|
}
|
||||||
|
withNextLocation = []
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.currentLocation = latestAccurate
|
self.currentLocation = latestAccurate
|
||||||
|
|
||||||
withNextLocation?(.success(latestAccurate))
|
withNextLocation.forEach {
|
||||||
withNextLocation = nil
|
$0(.success(latestAccurate))
|
||||||
|
}
|
||||||
|
withNextLocation = []
|
||||||
}
|
}
|
||||||
|
|
||||||
public func locationManagerDidPauseLocationUpdates(_ manager: CLLocationManager) {
|
public func locationManagerDidPauseLocationUpdates(_ manager: CLLocationManager) {
|
||||||
|
@ -429,8 +439,10 @@ extension GeoMonitor: CLLocationManagerDelegate {
|
||||||
print("GeoMonitor's location manager failed: \(error)")
|
print("GeoMonitor's location manager failed: \(error)")
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
withNextLocation?(.failure(error))
|
withNextLocation.forEach {
|
||||||
withNextLocation = nil
|
$0(.failure(error))
|
||||||
|
}
|
||||||
|
withNextLocation = []
|
||||||
}
|
}
|
||||||
|
|
||||||
public func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
|
public func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
|
||||||
|
|
Loading…
Reference in New Issue