-2

I wrote the following function to add a map annotation that uses CLGeocoder() to resolve the location name given a coordinate. I use a defer block to get the resolved location name. However, the defer block seems to finish before the closure finishes. Why?

Below is my code:

func addAnnotation(gestureRecognizer: UIGestureRecognizer) {
    var locationNameStr = ""
    defer {
        let newPin = Pin(dictionary: locationDictionary, context: sharedContext)
        newPin.name = locationNameStr

        do {
            //persist the new pin to core data
            try sharedContext.save()
            showPinOnMap(newPin)
        } catch let error as NSError {
            print("error saving the new pin in context")
        }
    }

    // use CLGeocoder to resolve the location name as title of the annotation
    CLGeocoder().reverseGeocodeLocation(CLLocation(latitude: newCoordinate.latitude, longitude: newCoordinate.longitude), completionHandler: {(placemarks, error) -> Void in

        if error != nil {
            print("reverse geocoding failed with error: \(error)")
            //return
        } else if placemarks!.count > 0 {
            let firstPlace = placemarks![0] as CLPlacemark

            if firstPlace.country != nil {
                locationNameStr = "a place in \(firstPlace.country!)"
            }
            else if firstPlace.locality != nil {
                locationNameStr = "a place in \(firstPlace.locality!)"
            }
            print("location name: \(locationNameStr)")
        }
    })
}
mfaani
  • 33,269
  • 19
  • 164
  • 293
TonyW
  • 18,375
  • 42
  • 110
  • 183

2 Answers2

8

You misunderstand what a defer block does.

It is a block of code that gets executed when you exit the current scope.

When you execute an async method with a completion block, it returns immediately and execution goes on to to the next line. The called method takes your completion block and saves it for later.

Your method then finishes and execution passes out of the method's scope. The defer block gets executed. At some later time, the async method finishes it's background work and calls the completion handler that you passed to it. With an async method that will ALWAYS happen after the calling method returns.

@fqdn has the right idea. Put your cleanup code in the completion block. That's where it belongs.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
5

reverseGeocodeLocation(_:completionHandler:) executes asynchronously, it makes sense that the code in your defer block is executing before the closure passed as the completionHandler argument is invoked

is there a reason that the code in your defer block can't be moved in to your completion handler?

fqdn
  • 2,823
  • 1
  • 13
  • 16