记录CLLocationManager didFailWithError回调2次的问题

208 阅读1分钟

现在应用中经常用到定位,定位代码比较简单。调试的过程中发现在弹窗权限弹窗的时候就会触发didFailWithError,当用户拒绝定位就会触发第二次回调。

第一次的error:code == CLError.denied.rawValue && domain = kCLErrorDomain

class TZLocationManager: NSObject {

    typealias SuccessBlock = ([CLLocation]) -> Void

    typealias FailureBlock = (Error) -> Void

    private let locationManager = CLLocationManager()

    /// 定位成功的回调block
    private var successBlock: SuccessBlock?

    /// 定位失败的回调block
    private var failureBlock: FailureBlock?

    /// 一次性定位
    public func startLocation(successBlock: SuccessBlock?, failureBlock: FailureBlock?) {        self.successBlock = successBlock

        self.failureBlock = failureBlock

        locationManager.delegate = self

        locationManager.startUpdatingLocation()

        locationManager.requestWhenInUseAuthorization()

    }


    public func stopUpdatingLocation() {

        locationManager.stopUpdatingLocation()
    }
}


extension TZLocationManager: CLLocationManagerDelegate {

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {        
        successBlock?(locations)
        locationManager.delegate = nil
    }
    

    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        // 定位权限弹窗的时候, 可能失败回调了
        let err = error as NSError
        if err.code == CLError.denied.rawValue && err.domain = kCLErrorDomain {
            print("定位失败:没有定位权限?")
        }
        failureBlock?(error)
        locationManager.delegate = nil
    }

}

经过一番排查才发现定位的用法一直是错的。我们需要先判断是否有定位权限,没有就去请求权限,等有权限了再去请求定位。

if status == .authorizedWhenInUse || status == .authorizedAlways {                 
    locationManager.startUpdatingLocation()         
} else {            
    locationManager.requestWhenInUseAuthorization()
}

下边是正确用法。

class TZLocationManager: NSObject {

    typealias SuccessBlock = ([CLLocation]) -> Void

    typealias FailureBlock = (Error) -> Void

    private let locationManager = CLLocationManager()

    /// 定位成功的回调block
    private var successBlock: SuccessBlock?

    /// 定位失败的回调block
    private var failureBlock: FailureBlock?

    /// 一次性定位
    public func startLocation(successBlock: SuccessBlock?, failureBlock: FailureBlock?) {        self.successBlock = successBlock

        self.failureBlock = failureBlock

        locationManager.delegate = self
        
        let status = CLLocationManager.authorizationStatus()
        
        if status == .authorizedWhenInUse || status == .authorizedAlways {
            locationManager.startUpdatingLocation()
        } else {
            locationManager.requestWhenInUseAuthorization()
        }
    }

    public func stopUpdatingLocation() {
        locationManager.stopUpdatingLocation()
    }
}


extension TZLocationManager: CLLocationManagerDelegate {

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {        successBlock?(locations)

        locationManager.delegate = nil

    }


    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {

        // 定位权限弹窗的时候, 可能失败回调了        

        failureBlock?(error)

        locationManager.delegate = nil

    }

    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {

        switch status {

        case .authorizedAlways, .authorizedWhenInUse:

            locationManager.startUpdatingLocation()

        case .denied, .restricted:

            let error = NSError(domain: kCLErrorDomain, code: CLError.denied.rawValue)

            failureBlock?(error)

            locationManager.delegate = nil

        default:

            break

        }

    }

}