什么时候必须调用CLLocationManager requestAlwaysAuthorization?

问题描述 投票:0回答:1

我有一个正在处理的应用程序,它需要能够获取用户的后台位置并更新服务器。 问题是,我可以让它工作的唯一方法是在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)
    }
}
ios background location cllocationmanager
1个回答
1
投票

重要的是,一个 CLLocationManager 的线程中实例化,该线程与一个 RunLoop,否则委托方法将不会被调用。 确保这一点的一种方法是从主队列中实例化它。

handleAfterLoginEvent 是对您的单体的第一个引用,位置管理器实例是由一个无 RunLoop 和委托方法不被调用。

你可以提前从主队列中实例化单人,或者显式地将创建派遣到主队列中。

请注意,用户可以选择拒绝位置权限或授予 "使用时",尽管你要求的是 "总是"。在你的代码中,一个更好的检查可能是 status == .notDetermined 而非 != .authorizedAlways

© www.soinside.com 2019 - 2024. All rights reserved.