Swift - CLGeocoder reverseGeocodeLocation completionHandler closure
Solution 1
I found the answer I needed in this thread: Set address string with reverseGeocodeLocation: and return from method
The issue lies with the fact that reverseGeocodeLocation
is asynchronous, the method is returning a value before the completionBlock sets p
in my example.
As requested, here's my current code.
func showAddViewController(placemark:CLPlacemark){
self.performSegueWithIdentifier("add", sender: placemark)
}
func getPlacemarkFromLocation(location: CLLocation){
CLGeocoder().reverseGeocodeLocation(location, completionHandler:
{(placemarks, error) in
if error {println("reverse geodcode fail: \(error.localizedDescription)")}
let pm = placemarks as [CLPlacemark]
if pm.count > 0 { self.showAddPinViewController(placemarks[0] as CLPlacemark) }
})
}
I didn't want to take the NSNotificationCenter route because that would add unnecessary overhead, rather inside the completionHandler
closure I call upon another function and pass the CLPlacemark
generated by getPlacemarkFromLocation
as a parameter to keep things asynchronous since the function will be called after placemarks
is set the function (should) receive the placemark needed and execute the code you want. Hope what I said makes sense.
Solution 2
With these lines of Swift, you can print out fully the location's address:
func getLocationAddress(location:CLLocation) {
var geocoder = CLGeocoder()
println("-> Finding user address...")
geocoder.reverseGeocodeLocation(location, completionHandler: {(placemarks, error)->Void in
var placemark:CLPlacemark!
if error == nil && placemarks.count > 0 {
placemark = placemarks[0] as CLPlacemark
var addressString : String = ""
if placemark.ISOcountryCode == "TW" /*Address Format in Chinese*/ {
if placemark.country != nil {
addressString = placemark.country
}
if placemark.subAdministrativeArea != nil {
addressString = addressString + placemark.subAdministrativeArea + ", "
}
if placemark.postalCode != nil {
addressString = addressString + placemark.postalCode + " "
}
if placemark.locality != nil {
addressString = addressString + placemark.locality
}
if placemark.thoroughfare != nil {
addressString = addressString + placemark.thoroughfare
}
if placemark.subThoroughfare != nil {
addressString = addressString + placemark.subThoroughfare
}
} else {
if placemark.subThoroughfare != nil {
addressString = placemark.subThoroughfare + " "
}
if placemark.thoroughfare != nil {
addressString = addressString + placemark.thoroughfare + ", "
}
if placemark.postalCode != nil {
addressString = addressString + placemark.postalCode + " "
}
if placemark.locality != nil {
addressString = addressString + placemark.locality + ", "
}
if placemark.administrativeArea != nil {
addressString = addressString + placemark.administrativeArea + " "
}
if placemark.country != nil {
addressString = addressString + placemark.country
}
}
println(addressString)
}
})
}
Cheers!
Solution 3
Here is closure that worked for me -- it took awhile to get it to work. I think your problem is related to not initializing p with the correct initializer. I tried a few variations until I got this to work: self.placemark = CLPlacemark(placemark: stuff[0] as CLPlacemark)
geocoder.reverseGeocodeLocation(newLocation, completionHandler: {(stuff, error)->Void in
if error {
println("reverse geodcode fail: \(error.localizedDescription)")
return
}
if stuff.count > 0 {
self.placemark = CLPlacemark(placemark: stuff[0] as CLPlacemark)
self.addressLabel.text = String(format:"%@ %@\n%@ %@ %@\n%@",
self.placemark.subThoroughfare ? self.placemark.subThoroughfare : "" ,
self.placemark.thoroughfare ? self.placemark.thoroughfare : "",
self.placemark.locality ? self.placemark.locality : "",
self.placemark.postalCode ? self.placemark.postalCode : "",
self.placemark.administrativeArea ? self.placemark.administrativeArea : "",
self.placemark.country ? self.placemark.country : "")
}
else {
println("No Placemarks!")
return
}
})
EDIT:
moved better answer to its own answer.
Related videos on Youtube
AaronDancer
Updated on February 27, 2020Comments
-
AaronDancer about 4 years
What I'm trying to do is pass a
CLLocation
to the functiongetPlacemarkFromLocation
which then uses the passedCLLocation
throughreverseGeocodeLocation
to set theCLPlacemark?
that will be returned.I'm having issues creating the
completionHandler
closure inreverseGeocodeLocation
,it's throwing a compiler error/crash:In Swift,
CLGeocodeCompletionHandler
isCLGeocodeCompletionHandler = (AnyObject[]!, NSError!) -> Void
according to the documentationAnyObject[]!
is supposed to containCLPlacemark
objects just like the Objective-C version.Here's my current code:
class func getPlacemarkFromLocation(location:CLLocation)->CLPlacemark?{ var g = CLGeocoder() var p:CLPlacemark? g.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) in let pm = placemarks as? CLPlacemark[] if (pm && pm?.count > 0){ p = placemarks[0] as? CLPlacemark } }) return p? }
EDIT: It seems like the error had to do with
placemarks.count
withplacemarks
not being treated like an array. It compiles now, however I'm getting nothing but nil when trying to setp
inside thecompletionHandler
. I've checked theCLLocation
s being passed and they are valid.EDIT 2: After printing
placemarks
, I can confirm that it returns data. Howeverp
is still returning nil. -
AaronDancer almost 10 yearsIt does seem like
p
not being initialized is the issue, so I've changedp = placemarks[0] as? CLPlacemark
top = CLPlacemark(placemark: placemarks[0] as? CLPlacemark)
however p still returns nil. I tried initializingp
beforereverseGeocodeLocation
is called, however I get EXC_BAD_ACCESS when trying to change the value. I cannot use theCLPlacemark(placemark: CLPlacemark?)
initializer beforehand because I have noCLPlacemark
to give -
AaronDancer almost 10 yearsI've tried that as well however I get EXC_BAD_ACCESS when doing so.
-
AaronDancer almost 10 yearsI've tried it again, I'm getting the same result as your example above when using a non-optional value.
-
JohnInSea almost 10 yearsClosures for swift don't appear to work as I would expect. The only way I could get the inside value to "stick" outside the closure is if the placemark was a member variable of the controller. It would probably work with static/class variables if swift supported them (apparently not available in the beta). Note, I even tried to use a local array initialized outside of the closure. I added (appended) the placemark to the array in the closure. But outside the closure the array was still employ.
-
pxpgraphics almost 10 yearsCan you elaborate on how you've solved your issue? I've tried external functions and notifications and still am receiving EXC_BAD_ACCESS. Thanks in advance, this would help other devs transitioning to Swift.
-
pxpgraphics almost 10 yearssorry, I was using this syntax...
self!.geocoder.reverseGeocodeLocation(location, completionHandler:{ [weak self] (placemarks: [AnyObject]!, error: NSError!) -> Void in // Your code here. })
-
AaronDancer almost 10 yearsSure, I'll edit this post and add my current code and elaborate a bit more.
-
SomaMan over 6 yearsThis will still return nil - you're not realising that p is set after you return the value, as reverseGeocodeLocation works async