0

I have this view with a couple of labels and a map. In this view I'm displaying some information about a geo-location point:

  • The address
  • The map + the pin
  • A first label (calculated)
  • A second label (calculated)

Once the view is loading I need to do some calculation to update the labels. This calculation are taking few seconds (I need to call an API) so I've put them in a queue:

dispatch_async(dispatch_get_main_queue(), {
    let loc = CLLocationCoordinate2D(latitude: self.place!.location.latitude, longitude: self.place!.location.longitude)
    let tmp = Int(Geo.apiCall(self.currentPosition, coordTo: loc));
    self.label1.text = " = \(tmp) unit";
})

I've used the main thread dispatch_get_main_queue() because I need to update the label. The problem is that it's blocking the rendering of the map and it's also blocking the other async function from CLGeocoder that is gathering the address from the geo-location point.

let loc = CLLocation(latitude: self.place!.location.latitude, longitude: self.place!.location.longitude)

CLGeocoder().reverseGeocodeLocation(loc, completionHandler:
    {(placemarks, error) in
        if error != nil {
            println("reverse geodcode fail: \(error.localizedDescription)")
        }
        let pms = placemarks as [CLPlacemark]
        if pms.count > 0 {
            let pm = placemarks[0] as CLPlacemark
            self.address.text = ABCreateStringWithAddressDictionary(pm.addressDictionary, false)
        }
})

So the view is displayed correctly, but all the label are updated at the same time, few seconds after the view is displayed, and the map is loading only when the label are rendered.

What would be the best work around to avoid this blocking when the view is rendering?

maxwell2022
  • 2,818
  • 5
  • 41
  • 60
  • The purpose of using this API to do a block of work is to **not** do it on the main queue. Using `dispatch_async` onto the main queue like this totally defeats the purpose. Use the standard "dispatch to background, dispatch back to main" – borrrden Oct 27 '14 at 01:28
  • I know but I thought that updating the UI (label) was not possible in the background queue? – maxwell2022 Oct 27 '14 at 01:33
  • Ok I see what you mean. I have to dispatch the task (API call) to the background queue. And when the task is complete, it's dispatching back to the main thread. Do you have a piece of code to illustrate this? thanks – maxwell2022 Oct 27 '14 at 01:41
  • To complement the answers: a clean design would involve to make the call `Geo.apiCall` itself _asynchronous_. – CouchDeveloper Oct 27 '14 at 06:40

2 Answers2

3
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT) {
    //Background Thread
    let loc = CLLocationCoordinate2D(latitude: self.place!.location.latitude,         longitude: self.place!.location.longitude)
    let tmp = Int(Geo.apiCall(self.currentPosition, coordTo: loc));

    dispatch_async(dispatch_get_main_queue()) {
        //Run UI Updates
        self.label1.text = " = \(tmp) unit";
    }
}

Get a background thread to do the work that doesn't directly involve changing the ui, and then call the main queue when needed. credit to: Understanding dispatch_async

Edit: Another option is to use AFNetworking (for objective-c) Alamofire (for swift), which will asynchronously call your api, and you can change the label in the completion handler: https://github.com/AFNetworking/AFNetworking , tutorial: http://www.raywenderlich.com/59255/afnetworking-2-0-tutorial

EDIT-2: I wasn't paying attention and mixed the objective C calls with your swift declarations :P

Community
  • 1
  • 1
ssrobbi
  • 496
  • 1
  • 5
  • 15
0

You should move the calculate code to background queue, in the main queue just set the value, so you can change code like this:

__block let loc = CLLocationCoordinate2D(latitude: self.place!.location.latitude, longitude: self.place!.location.longitude)
__block let tmp = Int(Geo.apiCall(self.currentPosition, coordTo: loc));
dispatch_async(dispatch_get_main_queue(), {
self.label1.text = " = \(distForYou) unit";

})

Feng Lin
  • 670
  • 4
  • 8