Mike Jonas
Mike Jonas

Reputation: 333

Function returns before code finishes running in swift

I made a class function which contains a closure that reverse geocodes location coordinates into a address string. I initially had it written in a viewcontroller, but I need to run this code in a few spots.

My problem is that the class function returns the string before the closure runs, so the returning string is blank. What are ways I can fix this? And are there better ways to organize my code rather than using a class function?

import CoreLocation
class LocationUtil: CLLocation {
    class func getLocationAddress(location:CLLocation?) -> NSString {
        var geocoder = CLGeocoder()
        var addressString : String = ""

        if location != nil {
            geocoder.reverseGeocodeLocation(location, completionHandler: {(placemarks, error)->Void in
                var placemark:CLPlacemark!

                if error == nil && placemarks.count > 0 {
                    placemark = placemarks[0] as CLPlacemark

                    if placemark.subThoroughfare != nil {
                        addressString = placemark.subThoroughfare + " "
                    }
                    if placemark.thoroughfare != nil {
                        addressString = addressString + placemark.thoroughfare + ", "
                    }
                    if placemark.locality != nil {
                        addressString = addressString + placemark.locality + ", "
                    }
                    if placemark.administrativeArea != nil {
                        addressString = addressString + placemark.administrativeArea + " "
                    }
                    if placemark.postalCode != nil {
                        addressString = addressString + placemark.postalCode + " "
                    }
                    if placemark.country != nil {
                        addressString = addressString + placemark.country
                    }

                }
                println("Address in closure: \(addressString)") //prints addresss
            })
        }
        println("Address out of closure: \(addressString)") // doesn't print address
        return addressString //this returns nothing
    }
}

Upvotes: 3

Views: 2868

Answers (2)

Swapnil Luktuke
Swapnil Luktuke

Reputation: 10475

The geocoder.reverseGeocodeLocation runs asynchronously and calls the completionHandler only after it completes.

So, getting a return value is obviously not an option. Instead, accept closure as an input/argument for your class function

class func getLocationAddress(location: CLLocation, getLocCompletionHandler : (addressString : String?, error : NSError?) -> Void) {

       geocoder.reverseGeocodeLocation(location, completionHandler: {(placemarks, error)->Void in
            //Your current code
            //...
            //...
            println("Address: \(addressString)")
            //Instead of returning the address string, call the 'getLocCompletionHandler'
            getLocCompletionHandler(addressString: addressString, error: error)

        })
}

And when you call this function, instead of working with the return value, wait for the closure to be called:

LocationUtil.getLocationAddress(currentLocation, completionHandler: { (addressObject, error) -> Void in
    println("Address: \(addressString)")
    //OR handle the error appropriately
}

Read up on closures to get a better understanding of the concept.

As far as organisation of code goes, class function is a good choice in this case.

Upvotes: 9

milo526
milo526

Reputation: 5083

You should return the completionHandler inside your

geocoder.reverseGeocodeLocation(location, completionHandler: {(placemarks, error)->Void in

The completionHandler will be called when the function is finished. For this to work you should also make your own function return asynchronously.

This is needed because your reverseGeocodeLocation function is ran asynchronously. Your main thread will continue to run your getLocationAddress so it will return the string but it is empty because it is returned before the asynchronous function finished.

Upvotes: 4

Related Questions