Ios 何时必须调用CLLocationManager请求AlwaysAuthorization?
我有一个正在开发的应用程序,需要能够获取用户的后台位置并更新服务器。问题是,我能让它工作的唯一方法是在应用程序之前请求授权:didFinishLaunchingWithOptions finishes 如果我以后请求,应用程序将不会调用我的位置代理回调。这是一个已知的问题吗?如果是,我如何解决它 我们希望它成为服务器上配置的可选功能。某些用户将属于启用此功能的组。这需要我先让我们的用户登录,这样我就可以查看是否启用了此功能。这是我的问题。如果我们真的打算使用它,我只想提示获得位置许可 我已经用Strava等应用程序进行了测试,它们可以延迟请求位置许可,直到你开始录制活动。文档中没有提到任何关于何时请求的内容,只是在您开始更新位置之前必须进行请求。这应该是可能的,但我遗漏了一些东西Ios 何时必须调用CLLocationManager请求AlwaysAuthorization?,ios,background,location,cllocationmanager,Ios,Background,Location,Cllocationmanager,我有一个正在开发的应用程序,需要能够获取用户的后台位置并更新服务器。问题是,我能让它工作的唯一方法是在应用程序之前请求授权:didFinishLaunchingWithOptions finishes 如果我以后请求,应用程序将不会调用我的位置代理回调。这是一个已知的问题吗?如果是,我如何解决它 我们希望它成为服务器上配置的可选功能。某些用户将属于启用此功能的组。这需要我先让我们的用户登录,这样我就可以查看是否启用了此功能。这是我的问题。如果我们真的打算使用它,我只想提示获得位置许可 我已经用S
// 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)
}
}
从与RunLoop关联的线程实例化CLLocationManager非常重要,否则将不会调用委托方法。确保这一点的一种方法是从主队列实例化它
当handleAfterLoginEvent是对singleton的第一个引用时,location manager实例将从没有RunLoop的线程实例化,并且不会调用委托方法
您可以先从主队列实例化单例,也可以显式地将创建分派到主队列
请注意,即使您要求“始终”,用户也可以在使用时拒绝位置权限或授予。更好的检查代码的方法可能是状态==.notDetermined,而不是!=。您的代码看起来不正确。您已经实现了一个单例,但从未引用.shared。你描述的情况不正常。在我看来,一个对象在发布时超出了范围。此外,您还需要处理这样一种情况,即即使您始终请求,用户也只能在使用时授予。更好的检查是查看状态是否为.notDetermined,而不是不等于always。我更新了代码以引用共享实例。当我简化这个问题的代码库时,这是一个输入错误。如前所述,当我在应用程序结束时请求权限时,此代码可以正常工作:didFinishLaunchingWithOptions。如果我只是将第一个位置权限请求延迟到用户登录之后,它就不会返回任何位置。何时/如何调用handleAfterLoginEvent?该函数是否在主队列上调用?否,它在后台线程上接收SwiftEventBus事件。我认为你说的范围是对的。我在AppDelegate中创建了一个LocationManager实例,并在didFinishLaunchingWithOptions中将共享实例设置为它。这样做之后,我就可以在登录中请求权限了,它起作用了。谢谢你的帮助!