JeongHan Seock JeongHan Seock - 5 months ago 17
Swift Question

iOS - How can I return latitude and longitude?

I can't return latitude and longitude. I always get a 0.0 and 0.0.

What can I do to get these values?

Code :

func forwardGeocoding (address: String) -> (Double, Double) {
let geoCoder = CLGeocoder()

var latitude: Double = 0.0
var longitude: Double = 0.0

geoCoder.geocodeAddressString(address) { (placemarks: [CLPlacemark]?, error: NSError?) -> Void in

if error != nil {
print(error?.localizedDescription)
} else {
if placemarks!.count > 0 {
let placemark = placemarks![0] as CLPlacemark
let location = placemark.location

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

print("before : \(latitude, longitude)")

}
}
}

print("after : \(latitude, longitude)")
return (latitude, longitude)
}


This my viewDidLoad:

override func viewDidLoad() {
super.viewDidLoad()
forwardGeocoding("New York, NY, United States")

}


Result:

after : (0.0, 0.0)

before : (40.713054, -74.007228)

Answer

The problem with your code is that geocoding requests are asynchronous, so the return statement is executed before the geocoding results are actually retrieved.

I'd probably use one of two options to fix this. First, instead of returning a tuple, make your own completion handler, and call it after the placemark is found:

func forwardGeocoding (address: String, completion: (CLLocationCoordinate2D) -> Void) {
    let geoCoder = CLGeocoder()
    geoCoder.geocodeAddressString(address) { (placemarks: [CLPlacemark]?, error: NSError?) -> Void in

        if error != nil {
            print(error?.localizedDescription)
        } else {
            if placemarks!.count > 0 {
                let placemark = placemarks![0] as CLPlacemark
                let location = placemark.location
                completion(location.coordinate)
            }
        }
    }
}

Now when you call this function you can provide the completion with the relevant values from wherever you're calling the function.

If the function is actually a method in a class and it never needs to be called from another class, you could have it set properties of the class, and those properties could have didSet blocks. For example:

class SomeClass {
    var coordinates: CLLocationCoordinate2D {
        didSet {
            doSomethingWithCoordinates()
        }
    }

    private func forwardGeocoding (address: String, completion: (CLLocationCoordinate2D) -> Void) {
        let geoCoder = CLGeocoder()
        geoCoder.geocodeAddressString(address) { (placemarks: [CLPlacemark]?, error: NSError?) -> Void in

            if error != nil {
                print(error?.localizedDescription)
            } else {
                if placemarks!.count > 0 {
                    let placemark = placemarks![0] as CLPlacemark
                    let location = placemark.location
                    self.coordinates = location.coordinate
                }
            }
        }
    }
}

The first options is probably more versatile, but the second avoids having completion blocks withing completion blocks, which can sometimes become confusing to keep track of in your code.