2

i'm trying to use swift geocoding to get the city, but somehow the city only showup nested inside the method and when returned the variable is empty, here is the code i'm using.

class {
   var locationManager = CLLocationManager()
   var longitude = CLLocationDegrees()
   var latitude = CLLocationDegrees()
   var city = ""

  override func viewDidLoad() {
    super.viewDidLoad()

    setupLocation()

    var x = getLocation()
    print("\n\n x your city is: \(x)\n\n"); // 'x' is always empty

    if x == "paris" {
        print("\n\n your city is: \(x)\n\n"); // 'x' is always empty

      } 
    }


func getLocation() -> String {

    longitude = (locationManager.location?.coordinate.longitude)!
    latitude = (locationManager.location?.coordinate.latitude)!


    let location = CLLocation(latitude: latitude, longitude: longitude) 
    print(location)

    CLGeocoder().reverseGeocodeLocation(location, completionHandler: {(placemarks, error) -> Void in
        print(location)

        if error != nil {
            print("Reverse geocoder failed with error" + error!.localizedDescription)
            return
        }

        if placemarks!.count > 0 {
            let pm = placemarks![0]
            print("locality is \(pm.locality)")
            self.city = pm.locality!
            print(" city first \(self.city)") //contains a city
        }
        else {
            print("Problem with the data received from geocoder")
        }
    })

    print("city second \(city)") //empty every time
    return city

 }
}
bmike
  • 902
  • 1
  • 20
  • 37
iknowNothing
  • 299
  • 1
  • 2
  • 12
  • Possible duplicate of [Swift - CLGeocoder reverseGeocodeLocation completionHandler closure](http://stackoverflow.com/questions/24345296/swift-clgeocoder-reversegeocodelocation-completionhandler-closure) – PeterB Oct 31 '16 at 00:25

3 Answers3

2

As pointed out here, you have to add a completion handler to your method:

func getLocation(completion: @escaping (String) -> Void) {

    longitude = (locationManager.location?.coordinate.longitude)!
    latitude = (locationManager.location?.coordinate.latitude)!


    let location = CLLocation(latitude: latitude, longitude: longitude) 
    print(location)

    CLGeocoder().reverseGeocodeLocation(location, completionHandler: {(placemarks, error) -> Void in
        print(location)

        if error != nil {
            print("Reverse geocoder failed with error" + error!.localizedDescription)
            return
        }

        if placemarks!.count > 0 {
            let pm = placemarks![0]
            print("locality is \(pm.locality)")
            completion(pm.locality!)
        }
            else {
                print("Problem with the data received from geocoder")
        }
    })

 }

And then just do:

getLocation() {
    locality in
    self.city = locality
}
Community
  • 1
  • 1
prodevmx
  • 94
  • 3
1

You have stumbled upon a time issue. reverseGeocodeLocation is asynchronous, so the and the method returns before the closure is fully evaluated.

If you set breakpoints you would see that the

print("city second \(city)") //empty every time

line would trigger before the

print(" city first \(self.city)") //contains a city

one

PeterB
  • 58
  • 7
1

Problem:

reverseGeocodeLocation is an asynchronous method (it doesn't evaluate immediately and would take time to evaluate). Before reverseGeocodeLocation is completed getLocation will be completed.

Solution:

Modify the getLocation to accept a closure as a parameter. Inside the completion handler of reverseGeocodeLocation call that closure and pass that value of the city

user1046037
  • 14,608
  • 12
  • 79
  • 117