Swift 提供当前 CLLocation 的函数(闭包不是委托)
Swift func to provide current CLLocation (with closures NOT delegates)
我想创建一个这样的 Swift 函数:
static func queryLocation(desiredAccuracy: CLLocationAccuracy, completion: @escaping (Result<CLLocation, Error>)->())
这个函数会尽可能简单。作为调用者,您不需要保留位置管理器,也不需要实现任何委托方法。只需询问位置并实现完成处理程序即可。
(删除原始源代码并添加为答案,因为它确实有效 - 我只是在正确调用它时遇到问题)
对不起大家。调用方法有问题。 OneTimeLocation class 实际上按预期工作。 (也可作为 Swift 包装:https://github.com/hoereth/CoreLocationUtils/blob/main/Sources/CoreLocationUtils/OneTimeLocation.swift)
import Foundation
import CoreLocation
public class OneTimeLocation : NSObject, CLLocationManagerDelegate {
public enum LocationError : Error {
case denied
case restricted
case timeout
case unknown
}
let manager: CLLocationManager
let completion: (Result<CLLocation, Error>)->()
let timeout: TimeInterval
fileprivate static let instancesQueue = DispatchQueue(label: "OneTimeLocation.instances")
fileprivate static var instances = Set<OneTimeLocation>()
/// Will either find you the current location or produce an error.
/// - Parameters:
/// - desiredAccuracy: see CLLocationManager.desiredAccuracy
/// - timeout: Applies to actual finding a location. Dialogs are presented without timeout.
public static func queryLocation(desiredAccuracy: CLLocationAccuracy, timeout: TimeInterval, completion: @escaping (Result<CLLocation, Error>)->()) {
let oneTimeLocation = OneTimeLocation(desiredAccuracy: desiredAccuracy, completion: completion, timeout: timeout)
oneTimeLocation.manager.delegate = oneTimeLocation
switch CLLocationManager.authorizationStatus() {
case .authorizedAlways, .authorizedWhenInUse:
instancesQueue.sync {
_ = instances.insert(oneTimeLocation)
oneTimeLocation.manager.startUpdatingLocation()
DispatchQueue.main.asyncAfter(deadline: .now() + timeout) {
oneTimeLocation.manager.stopUpdatingLocation()
completion(Result.failure(LocationError.timeout))
oneTimeLocation.removeInstance()
}
}
case .notDetermined:
instancesQueue.sync {
_ = instances.insert(oneTimeLocation)
oneTimeLocation.manager.requestWhenInUseAuthorization()
}
case .denied:
completion(Result.failure(LocationError.denied))
case .restricted:
completion(Result.failure(LocationError.restricted))
@unknown default:
completion(Result.failure(LocationError.unknown))
}
}
fileprivate init(desiredAccuracy: CLLocationAccuracy, completion: @escaping (Result<CLLocation, Error>)->(), timeout: TimeInterval) {
self.manager = CLLocationManager()
self.manager.desiredAccuracy = desiredAccuracy
self.completion = completion
self.timeout = timeout
}
public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if (locations.count > 0) {
self.completion(Result.success(locations[0]))
} else {
self.completion(Result.failure(LocationError.unknown))
}
self.manager.stopUpdatingLocation()
self.removeInstance()
}
public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
self.completion(Result.failure(error))
self.manager.stopUpdatingLocation()
self.removeInstance()
}
public func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch CLLocationManager.authorizationStatus() {
case .authorizedAlways, .authorizedWhenInUse:
self.manager.startUpdatingLocation()
DispatchQueue.main.asyncAfter(deadline: .now() + timeout) {
self.manager.stopUpdatingLocation()
self.completion(Result.failure(LocationError.timeout))
self.removeInstance()
}
case .notDetermined:
break;
case .denied:
completion(Result.failure(LocationError.denied))
self.removeInstance()
case .restricted:
completion(Result.failure(LocationError.restricted))
self.removeInstance()
@unknown default:
completion(Result.failure(LocationError.unknown))
self.removeInstance()
}
}
fileprivate func removeInstance() {
Self.instancesQueue.sync {
_ = OneTimeLocation.instances.remove(self)
}
}
}
我想创建一个这样的 Swift 函数:
static func queryLocation(desiredAccuracy: CLLocationAccuracy, completion: @escaping (Result<CLLocation, Error>)->())
这个函数会尽可能简单。作为调用者,您不需要保留位置管理器,也不需要实现任何委托方法。只需询问位置并实现完成处理程序即可。
(删除原始源代码并添加为答案,因为它确实有效 - 我只是在正确调用它时遇到问题)
对不起大家。调用方法有问题。 OneTimeLocation class 实际上按预期工作。 (也可作为 Swift 包装:https://github.com/hoereth/CoreLocationUtils/blob/main/Sources/CoreLocationUtils/OneTimeLocation.swift)
import Foundation
import CoreLocation
public class OneTimeLocation : NSObject, CLLocationManagerDelegate {
public enum LocationError : Error {
case denied
case restricted
case timeout
case unknown
}
let manager: CLLocationManager
let completion: (Result<CLLocation, Error>)->()
let timeout: TimeInterval
fileprivate static let instancesQueue = DispatchQueue(label: "OneTimeLocation.instances")
fileprivate static var instances = Set<OneTimeLocation>()
/// Will either find you the current location or produce an error.
/// - Parameters:
/// - desiredAccuracy: see CLLocationManager.desiredAccuracy
/// - timeout: Applies to actual finding a location. Dialogs are presented without timeout.
public static func queryLocation(desiredAccuracy: CLLocationAccuracy, timeout: TimeInterval, completion: @escaping (Result<CLLocation, Error>)->()) {
let oneTimeLocation = OneTimeLocation(desiredAccuracy: desiredAccuracy, completion: completion, timeout: timeout)
oneTimeLocation.manager.delegate = oneTimeLocation
switch CLLocationManager.authorizationStatus() {
case .authorizedAlways, .authorizedWhenInUse:
instancesQueue.sync {
_ = instances.insert(oneTimeLocation)
oneTimeLocation.manager.startUpdatingLocation()
DispatchQueue.main.asyncAfter(deadline: .now() + timeout) {
oneTimeLocation.manager.stopUpdatingLocation()
completion(Result.failure(LocationError.timeout))
oneTimeLocation.removeInstance()
}
}
case .notDetermined:
instancesQueue.sync {
_ = instances.insert(oneTimeLocation)
oneTimeLocation.manager.requestWhenInUseAuthorization()
}
case .denied:
completion(Result.failure(LocationError.denied))
case .restricted:
completion(Result.failure(LocationError.restricted))
@unknown default:
completion(Result.failure(LocationError.unknown))
}
}
fileprivate init(desiredAccuracy: CLLocationAccuracy, completion: @escaping (Result<CLLocation, Error>)->(), timeout: TimeInterval) {
self.manager = CLLocationManager()
self.manager.desiredAccuracy = desiredAccuracy
self.completion = completion
self.timeout = timeout
}
public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if (locations.count > 0) {
self.completion(Result.success(locations[0]))
} else {
self.completion(Result.failure(LocationError.unknown))
}
self.manager.stopUpdatingLocation()
self.removeInstance()
}
public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
self.completion(Result.failure(error))
self.manager.stopUpdatingLocation()
self.removeInstance()
}
public func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch CLLocationManager.authorizationStatus() {
case .authorizedAlways, .authorizedWhenInUse:
self.manager.startUpdatingLocation()
DispatchQueue.main.asyncAfter(deadline: .now() + timeout) {
self.manager.stopUpdatingLocation()
self.completion(Result.failure(LocationError.timeout))
self.removeInstance()
}
case .notDetermined:
break;
case .denied:
completion(Result.failure(LocationError.denied))
self.removeInstance()
case .restricted:
completion(Result.failure(LocationError.restricted))
self.removeInstance()
@unknown default:
completion(Result.failure(LocationError.unknown))
self.removeInstance()
}
}
fileprivate func removeInstance() {
Self.instancesQueue.sync {
_ = OneTimeLocation.instances.remove(self)
}
}
}