Ios 何时必须调用CLLocationManager请求AlwaysAuthorization?

Ios 何时必须调用CLLocationManager请求AlwaysAuthorization?,ios,background,location,cllocationmanager,Ios,Background,Location,Cllocationmanager,我有一个正在开发的应用程序,需要能够获取用户的后台位置并更新服务器。问题是,我能让它工作的唯一方法是在应用程序之前请求授权:didFinishLaunchingWithOptions finishes 如果我以后请求,应用程序将不会调用我的位置代理回调。这是一个已知的问题吗?如果是,我如何解决它 我们希望它成为服务器上配置的可选功能。某些用户将属于启用此功能的组。这需要我先让我们的用户登录,这样我就可以查看是否启用了此功能。这是我的问题。如果我们真的打算使用它,我只想提示获得位置许可 我已经用S

我有一个正在开发的应用程序,需要能够获取用户的后台位置并更新服务器。问题是,我能让它工作的唯一方法是在应用程序之前请求授权:didFinishLaunchingWithOptions finishes

如果我以后请求,应用程序将不会调用我的位置代理回调。这是一个已知的问题吗?如果是,我如何解决它

我们希望它成为服务器上配置的可选功能。某些用户将属于启用此功能的组。这需要我先让我们的用户登录,这样我就可以查看是否启用了此功能。这是我的问题。如果我们真的打算使用它,我只想提示获得位置许可

我已经用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)
    }
}
从与RunLoop关联的线程实例化CLLocationManager非常重要,否则将不会调用委托方法。确保这一点的一种方法是从主队列实例化它

当handleAfterLoginEvent是对singleton的第一个引用时,location manager实例将从没有RunLoop的线程实例化,并且不会调用委托方法

您可以先从主队列实例化单例,也可以显式地将创建分派到主队列


请注意,即使您要求“始终”,用户也可以在使用时拒绝位置权限或授予。更好的检查代码的方法可能是状态==.notDetermined,而不是!=。您的代码看起来不正确。您已经实现了一个单例,但从未引用.shared。你描述的情况不正常。在我看来,一个对象在发布时超出了范围。此外,您还需要处理这样一种情况,即即使您始终请求,用户也只能在使用时授予。更好的检查是查看状态是否为.notDetermined,而不是不等于always。我更新了代码以引用共享实例。当我简化这个问题的代码库时,这是一个输入错误。如前所述,当我在应用程序结束时请求权限时,此代码可以正常工作:didFinishLaunchingWithOptions。如果我只是将第一个位置权限请求延迟到用户登录之后,它就不会返回任何位置。何时/如何调用handleAfterLoginEvent?该函数是否在主队列上调用?否,它在后台线程上接收SwiftEventBus事件。我认为你说的范围是对的。我在AppDelegate中创建了一个LocationManager实例,并在didFinishLaunchingWithOptions中将共享实例设置为它。这样做之后,我就可以在登录中请求权限了,它起作用了。谢谢你的帮助!