我有一个正在处理的应用程序,它需要能够获取用户的后台位置并更新服务器。 问题是,我可以让它工作的唯一方法是在application:didFinishLaunchingWithOptions完成之前请求AlwaysAuthorization。
如果我稍后请求,应用程序就不会调用我的位置委托回调。 这是否是一个已知的问题,如果是,我如何解决它。
我们的愿望是让它成为一个可选的功能,在我们的服务器上配置。 一些用户将属于一个组,该组将启用该功能。 这就需要我先让我们的用户登录,这样我才能看到这个功能是否启用。 这就是我的问题所在。 我希望只有在我们真正打算使用时才会提示位置权限。
我已经用Strava等应用进行了测试,他们能够延迟请求位置权限,直到你开始记录一个活动。 文档中并没有提到任何关于何时请求的内容,只是说必须在你开始更新位置之前发生。 这应该是可能的,但是我漏掉了一些东西。
// Methods from AppDelegate...
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
...
// Normal initialization code goes here
...
// Requesting alwaysAuthorization here works
LocationManager.shared.requestPermission()
return true
}
func handleAfterLoginEvent() {
// Requesting alwaysAuthorization here, instead of in didFinishLaunchingWithOptions doesn't work
LocationManager.shared.requestPermission()
}
func handleServerPushRequestForLocation() {
// Called when push notification is received.
LocationManager.shared.reportCurrentLocation()
}
// End AppDelegate methods
public class LocationManager {
public static let shared = LocationManager()
private var locationManager = CLLocationManager()
private var delegate = LocationDelegate()
init() {
locationManager.delegate = delegate
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.allowsBackgroundLocationUpdates = true
locationManager.pausesLocationUpdatesAutomatically = false
}
deinit {
locationManager.delegate = nil
}
public func requestPermission() {
print("requestPermission called")
let status: CLAuthorizationStatus = CLLocationManager.authorizationStatus()
LocationManager.print(status: status)
if status != .authorizedAlways {
print("Requesting always authorization for LocationManager")
locationManager.requestAlwaysAuthorization()
}
}
public func reportCurrentLocation() {
print("reportCurrentLocation called")
guard CLLocationManager.locationServicesEnabled() else {
print("Skipping location update. Location services aren't enabled.")
return
}
print("requestLocation")
locationManager.requestLocation()
}
public static func print(status: CLAuthorizationStatus) {
switch status {
case .notDetermined : print("LocationManager: notDetermined")
case .authorizedWhenInUse : print("LocationManager: authorizedWhenInUse")
case .authorizedAlways : print("LocationManager: authorizedAlways")
case .restricted : print("LocationManager: restricted")
case .denied : print("LocationManager: denied")
default : print("LocationManager: unknown")
}
}
}
public class LocationDelegate: NSObject, CLLocationManagerDelegate {
public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("LocationManager didFailWithError: \(error.localizedDescription)")
}
public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let currentLocation = locations.last
print("LocationManager lat: \(currentLocation?.coordinate.latitude.description ?? "nil"), long: \(currentLocation?.coordinate.longitude.description ?? "nil"), horizontalAccuracy: \(currentLocation?.horizontalAccuracy.debugDescription ?? "nil"), time: \(currentLocation?.timestamp.debugDescription ?? "nil")")
postNotification(currentLocation)
}
public func locationManagerDidPauseLocationUpdates(_ manager: CLLocationManager) {
print("LocationManager locationManagerDidPauseLocationUpdates")
}
public func locationManagerDidResumeLocationUpdates(_ manager: CLLocationManager) {
print("LocationManager locationManagerDidResumeLocationUpdates")
}
public func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
LocationManager.print(status: status)
}
private func postNotification(_ currentLocation: CLLocation?) {
let content = UNMutableNotificationContent()
content.title = "New location reported"
content.body = "LocationManager lat: \(currentLocation?.coordinate.latitude.description ?? "nil"), long: \(currentLocation?.coordinate.longitude.description ?? "nil"), horizontalAccuracy: \(currentLocation?.horizontalAccuracy.debugDescription ?? "nil"), time: \(currentLocation?.timestamp.debugDescription ?? "nil")"
content.sound = UNNotificationSound.default
content.categoryIdentifier = "notify-new-location"
let request = UNNotificationRequest.init(identifier: "notify-new-location", content: content, trigger: nil)
let center = UNUserNotificationCenter.current()
center.add(request)
}
}
重要的是,一个 CLLocationManager
的线程中实例化,该线程与一个 RunLoop
,否则委托方法将不会被调用。 确保这一点的一种方法是从主队列中实例化它。
当 handleAfterLoginEvent
是对您的单体的第一个引用,位置管理器实例是由一个无 RunLoop
和委托方法不被调用。
你可以提前从主队列中实例化单人,或者显式地将创建派遣到主队列中。
请注意,用户可以选择拒绝位置权限或授予 "使用时",尽管你要求的是 "总是"。在你的代码中,一个更好的检查可能是 status == .notDetermined
而非 != .authorizedAlways