2

I want to wrap the geocoder.geocodeAddressString in another method with other logic.

public func placemarkForSearchResult<T>(searchResult: T) -> CLPlacemark? {
        if let searchResult = searchResult as? String {
            let geocoder = CLGeocoder()
            geocoder.geocodeAddressString(searchResult, completionHandler: {
                (placemarks, error) -> Void in
                
                // Check for returned placemarks
                if let placemarks = placemarks where placemarks.count > 0 {
                    return placemarks[0] as! CLPlacemark // CLPlacemark is not convertible to void error message
                }
                return nil // Typd Void does not conform to protocol NilLiteralConvertible
            })
        }
    }

I have some other logic in this method that's not really relevant, but I was wondering how I can handle a situation like this where I want to return a CLPlacemark, but cannot because the completionHandler for the geocoder returns Void. I cannot change the Void parameter of the geocoder callback.

Is that possible? Or am I stuck with calling a delegate method that uses the found CLPlacemark from the geocoder?

halfer
  • 19,824
  • 17
  • 99
  • 186
Crystal
  • 28,460
  • 62
  • 219
  • 393

1 Answers1

6

You can never return a placemark from your placemarkForSearchResult function, because obtaining a placemark requires the use of an asynchronous function (geocodeAddressString). By that time that function has finished and calls back into your completion handler, your placemarkForSearchResult has already finished and returned long ago!

You need a strategy compatible with the asynchronous nature of the function you are calling. Instead of returning a placemark from placemarkForSearchResult, you need placemarkForSearchResult to accept a callback function parameter. When you have your placemark in the completion handler, you call that callback function. If that callback function is (cleverly) designed to accept a placemark parameter, you are now handing that placemark to whoever called you in the first place.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • And see my answer here: http://stackoverflow.com/a/30133533/341994 - indeed, your question is effectively a duplicate of that one. – matt Jun 26 '15 at 04:01
  • "Or am I stuck with calling a delegate method that uses the found CLPlacemark from the geocoder" Yes, that's another possible strategy. They are almost the same: in both approaches, you are establishing a line of communication back to your caller, so that _you_ can send the placemark back to your caller in your own sweet asynchronous time. But the "accept a callback function" approach is more lightweight and more flexible, since there is no need to impose a delegate-protocol architecture on your caller. – matt Jun 26 '15 at 04:03
  • just a follow up snippet, if my completion would look like: `completion: (CLPlacemark) -> Void` , how do you pass the callback to the original `placemarkForSearchResult` function since I don't have the actual placemark yet. Let's say it's supposed to be, `addPlacemark(placemark: CLPlacemark)`, then how do I pass that as a callback? – Crystal Jun 26 '15 at 04:44
  • 1
    So now you are playing the role of the caller? You would pass `addPlacemark`. But you are more likely to pass an anonymous function, i.e. `{ placemark in // ... code }`. In other words, the way you, as caller, will call `placemarkForSearchResult` is just like the way you are already calling `geocoder.geocodeAddressString`. – matt Jun 26 '15 at 04:46
  • 1
    If you don't understand how functions get passed around as values in Swift (though I'm pretty sure you _do_ understand it), see my book, starting about here: http://www.apeth.com/swiftBook/ch02.html#_function_as_value – matt Jun 26 '15 at 04:48