iOS/Sources/Shared/Location/CLLocation+Sanitize.swift

62 lines
2.2 KiB
Swift

import CoreLocation
extension CLLocation {
private enum SanitizeFailure: Error {
case invalid(String)
case invalidKeyPath
}
func sanitized() throws -> CLLocation {
// FB9030164 iOS 14.5 (at least betas) started reporting vertical accuracy as non-finite
let doubleKeyPaths: [KeyPath<CLLocation, Double>: Result<Double, SanitizeFailure>] = [
\.horizontalAccuracy: .failure(.invalid("horizontalAccuracy")),
\.verticalAccuracy: .success(-1),
\.altitude: .success(0),
\.coordinate.latitude: .failure(.invalid("latitude")),
\.coordinate.longitude: .failure(.invalid("longitude")),
\.course: .success(-1),
\.speed: .success(-1),
\.speedAccuracy: .success(-1),
\.courseAccuracy: .success(-1),
]
func isValid(_ value: Double) -> Bool {
value.isFinite && !value.isNaN
}
func sanitize(_ keyPath: KeyPath<CLLocation, Double>) throws -> Double {
let value = self[keyPath: keyPath]
if isValid(value) {
return value
} else {
if let fallback = doubleKeyPaths[keyPath] {
return try fallback.get()
} else {
throw SanitizeFailure.invalidKeyPath
}
}
}
let needsSanitization = !doubleKeyPaths.keys.allSatisfy({ isValid(self[keyPath: $0]) })
guard needsSanitization else { return self }
let sanitizedCoordinate = try CLLocationCoordinate2D(
latitude: sanitize(\.coordinate.latitude),
longitude: sanitize(\.coordinate.longitude)
)
return try CLLocation(
coordinate: sanitizedCoordinate,
altitude: sanitize(\.altitude),
horizontalAccuracy: sanitize(\.horizontalAccuracy),
verticalAccuracy: sanitize(\.verticalAccuracy),
course: sanitize(\.course),
courseAccuracy: sanitize(\.courseAccuracy),
speed: sanitize(\.speed),
speedAccuracy: sanitize(\.speedAccuracy),
timestamp: timestamp
)
}
}