ggworean ggworean - 6 months ago 66
Swift Question

Swift semaphore not waiting for function to finish before calling UI

I'm trying to delay a segue until I get a response from a

reverseGeocodeLocation
call. However, when using breakpoints to check when the value actually changes, it's still happening after the UI transition occurs. I've tried having the function be a void and with the current
String
return.

EDITED CODE:

func getReversedGeocodeLocation(completionHandler: (String, NSError?) ->()) {

let semaphore = dispatch_semaphore_create(0)

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> Void in

CLGeocoder().reverseGeocodeLocation(self.newMeetupLocation, completionHandler: {(placemarks, error) -> Void in

if error != nil {
print("Reverse geocoder failed with error" + error!.localizedDescription)
return
}
else if placemarks?.count > 0 {

}
else {
print("Problem with the data received from geocoder")
}

completionHandler(placemarks!.first!.name! ?? "", error)
})

dispatch_semaphore_signal(semaphore)

}
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
}


OLD CODE:

let semaphore = dispatch_semaphore_create(1) //even with 0, it's not working

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> Void in

self.newAddress = self.getReversedGeocodeLocation()

dispatch_semaphore_signal(semaphore)
}

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)

//dispatch_semaphore_signal(semaphore)

print(self.newAddress + ".")

self.performSegueWithIdentifier("mainToNewAddress", sender: self)

func getReversedGeocodeLocation() -> String{

var address = ""
CLGeocoder().reverseGeocodeLocation(self.newAddressLocation, completionHandler: {(placemarks, error) -> Void in

if error != nil {
print("Reverse geocoder failed with error" + error!.localizedDescription)
return
}
else if placemarks?.count > 0 {
let pm = placemarks?.first!
address = pm!.name!
}
else {
print("Problem with the data received from geocoder")
}
})

return address
}

Answer

Using a semaphore and dispatching the call to getReversedGeocodeLocation is unnecessarily complicated. CLGeocoder().reverseGeocodeLocation is already asynchronous. If you simply pass a completion handler closure to getReversedGeocodeLocation then you can use that to invoke the segue;

self.getReversedGeocodeLocation(self.newAddressLocation, completionHandler: { (address,error) in 
    guard error == nil else {
        print("Reverse geocoder failed with error" + error!.localizedDescription)
        return
    }

    guard let address = address else {
        print("No address returned")
        return
    }

    self.newAddress = address
    dispatch_async(dispatch_get_main_queue(), {
        self.performSegueWithIdentifier("mainToNewAddress", sender: self)
    })
})

func getReversedGeocodeLocation(addressLocation: String, completionHandler:((address: String?, error: NSError?) -> Void))) {
    CLGeocoder().reverseGeocodeLocation(self.newAddressLocation, completionHandler: {(placemarks, error) -> Void in

        var address = nil

        if placeMarks.count > 0 {
            if let pm = placeMarks!.first {
                address  = pm.name
            }
        } else {
            print("Problem with the data received from geocoder")
        }

        completionHandler(address,error)
    })
}