在继续

时间:2016-12-06 04:03:14

标签: swift escaping closures core-location

我写了一个类UserLocation(下面),它使用CoreLocation来获取用户的当前位置(字符串)&纬度和经度(也是一个字符串" lat,long"用于传递给API)。

我的代码可以工作,但是当我初始化类时,我的其余代码在继续之前不会等待init完成,所以我错过了分配与位置相关的值的机会。 39;我试图检索。

这是一个合理的方法吗(或者我应该考虑其他更多的" MVC"组织),如果是,我怎样才能让我的代码等待初始化(使用location find& amp;反向地理编码)在继续前完成。有没有办法将代码放在初始化之下的某种@escaping闭包中,这个闭包是在类初始化中指定的?我很快乐,所以感谢你的善意。

在ViewController.swift的viewDidAppear()中:

let userLocation = UserLocation() // initializes properly but code below doesn't wait.
locationsArray[0].name = userLocation.place
locationsArray[0].coordinates = userLocation.coordinates

我的UserLocation.swift类:

import Foundation
import CoreLocation

class UserLocation {
    var place = ""
    var coordinates = ""

    let locationManager = CLLocationManager()
    var currentLocation: CLLocation!

    init() {
        returnResults()
    }

    func returnResults () {
        getUserLocation { placemark in
            if placemark != nil {
                self.place = (placemark?.name)!
                self.coordinates = "\((placemark?.location?.coordinate.latitude)!),\((placemark?.location?.coordinate.longitude)!)"
            } else {
                print("Error retrieving placemark")
            }
        }
    }

    func getUserLocation(completion: @escaping (CLPlacemark?) -> ()) {
        var placemark: CLPlacemark?

        locationManager.requestWhenInUseAuthorization() 

        if (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedWhenInUse ||
            CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedAlways) {
            currentLocation = locationManager.location

            let geoCoder = CLGeocoder()
            geoCoder.reverseGeocodeLocation(currentLocation) { (placemarks, error) -> Void in

                if error != nil {
                    print("Error getting location: \(error)")
                    placemark = nil
                } else {
                    placemark = placemarks?.first
                }
                completion(placemark)
            }
        }
    }
}

extension CLPlacemark {
    var cityState: String {
        var result = ""
        switch (self.locality, self.administrativeArea, self.country) {
        case (.some, .some, .some("United States")):
            result = "\(locality!), \(administrativeArea!)"
        case (.some, _ , .some):
            result = "\(locality!), \(country!)"
        default:
            result = name ?? "Location Unknown"
        }
        return result
    }
}

1 个答案:

答案 0 :(得分:3)

这不一定是Swift问题。您的问题是由于returnResults以异步方式执行变量设置,因为它调用异步函数 - getUserLocation,与reverseGeocodeLocation异步是异步(这是CoreLocation的方式)工作 - 您不能同步获取位置,但在回调中。

您不希望等待returnResults执行回调,因为这意味着在CoreLocation初始化并尝试确定位置时阻止主线程。相反,您应该使用returnResults可用于表示位置检索完成的完成块来遵循异步模式。

以上的一个例子是:

class UserLocation {
    var place = ""
    var coordinates = ""

    let locationManager = CLLocationManager()
    var currentLocation: CLLocation!

    init() {
       // don't call anymore from here, let the clients ask for the locations 
    }

    // This was renamed from returnResults to a more meaningful name
    // Using the Bool in the completion to signal the success/failure
    // of the location retrieval
    func updateLocations(withCompletion completion: @escaping (Bool) -> Void) {
        getUserLocation { placemark in
            if placemark != nil {
                self.place = (placemark?.name)!
                self.coordinates = "\((placemark?.location?.coordinate.latitude)!),\((placemark?.location?.coordinate.longitude)!)"
                completion(true)
            } else {
                print("Error retrieving placemark")
                completion(false)
            }
        }
    }
...

然后,您可以将被调用的代码修改为:

let userLocation = UserLocation()
userLocation.updateLocations { success in
    guard success else { return }
    locationsArray[0].name = userLocation.place
    locationsArray[0].coordinates = userLocation.coordinates
}

您不会阻止主线程,并在位置可用时执行相应的代码。