Reputation: 23
I have now tried lots of things, but none of them seem to work.
I have a for loop which parses some data and converts coordinates into ZIP string:
for i in 0 ... results.count - 1
{
result = results[i]
self.coordinateToString(lat: result.lat, long: result.long, completion: { (place) in
someCell.label.text = place
})
}
func coordinateToString(lat: Double, long: Double, completion: @escaping (String) -> ()) {
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: lat, longitude: long)
var ret = ""
geoCoder.reverseGeocodeLocation(location, completionHandler:
{
placemarks, error -> Void in
guard let placeMark = placemarks?.first else { return }
if let zip = placeMark.postalCode, let town = placeMark.subAdministrativeArea
{
let toAppend = "\(zip)" + " \(town)"
ret = toAppend
}
})
DispatchQueue.main.async {
completion(ret)
}
}
However I never manage to show the correct place in the cell, it always shows empty space because it somehow doesn't wait for the completion handler to finish converting. What am I doing wrong here?
Upvotes: 1
Views: 473
Reputation: 4099
This happens because reverseGeocodeLocation
returns right away and its completion handler runs afterwards. This means that ret
value may be empty when it gets put on the main queue. You should dispatch to main from within the callback, like so:
func coordinateToString(lat: Double, long: Double, completion: @escaping (String) -> ()) {
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: lat, longitude: long)
var ret = ""
geoCoder.reverseGeocodeLocation(location, completionHandler:
{
placemarks, error -> Void in
guard let placeMark = placemarks?.first else { return }
if let zip = placeMark.postalCode, let town = placeMark.subAdministrativeArea
{
let toAppend = "\(zip)" + " \(town)"
ret = toAppend
DispatchQueue.main.async {
completion(ret)
}
}
})
Of course, given this scenario, you need to handle error cases accordingly. Better yet, use defer
, that way completion gets called regardless of what happens:
func coordinateToString(lat: Double, long: Double, completion: @escaping (String) -> ()) {
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: lat, longitude: long)
var ret = ""
geoCoder.reverseGeocodeLocation(location, completionHandler:
{
defer {
DispatchQueue.main.async {
completion(ret)
}
}
placemarks, error -> Void in
guard let placeMark = placemarks?.first else { return }
if let zip = placeMark.postalCode, let town = placeMark.subAdministrativeArea
{
let toAppend = "\(zip)" + " \(town)"
ret = toAppend
}
})
Upvotes: 2