Swift Reverse Geocoding using the Data - swift

I am successfully getting the current address details based on my location. It printlns perfectly. What is throwing me is how I extract the data from this call. I have tried passing, say the ZIP/Postcode, as local and even global variables but with no joy. The data only seems to exist within this call. How can I use it elsewhere?
// Get Address Information
let geoCoder = CLGeocoder()
let newLocation = CLLocation(latitude: valueLatitude, longitude: valueLongitude)
geoCoder.reverseGeocodeLocation(newLocation, completionHandler: {(placemarks: [AnyObject]!, error: NSError!) in
if error != nil {
println("Geocode failed with error: \(error.localizedDescription)")
}
if placemarks.count > 0 {
let placemark = placemarks[0] as! CLPlacemark
let addressDictionary = placemark.addressDictionary
let address = addressDictionary[kABPersonAddressStreetKey] as! NSString
let city = addressDictionary[kABPersonAddressCityKey] as! NSString
let state = addressDictionary[kABPersonAddressStateKey] as! NSString
let postcode = addressDictionary[kABPersonAddressZIPKey] as! NSString
let country = addressDictionary[kABPersonAddressCountryKey] as! NSString
println("\(address) \(city) \(state) \(postcode) \(country)") }
})

Your problem is most likely due to the fact that reverseGeocodeLocation is an asynchronous request made to Apple servers.
What needs to happen is:
You call reverseGeocodeLocation
reverseGeocodeLocation finishes, starts its completion which calls a method passing the placemark you just recovered.
In order to do that:
#IBAction func btnInsertClicked(sender: AnyObject) {
var locationRecord: LocationRecord = LocationRecord()
// Get Address Information
let geoCoder = CLGeocoder()
let newLocation = CLLocation(latitude: valueLatitude, longitude: valueLongitude)
geoCoder.reverseGeocodeLocation(newLocation, completionHandler:
{(placemarks: [AnyObject]!, error: NSError!) in
if error != nil {
println("Geocode failed with error: (error.localizedDescription)")
}
if placemarks.count > 0 {
let myPlacemark = placemarks[0] as! CLPlacemark
// Here call the method that uses myPlacemark
self.myAwesomeMethod(placemarks[0] as! CLPlacemark)
} else {
println("No placemark")
}
})
}
Where you need to use it in your code:
func myAwesomeMethod(placemark: CLPlacemark) {
// Do stuff with placemark
}
I won't have my mac until tonight but if that doesn't work, leave a comment and we will work this out

Related

How to call CLGeocoder method synchronously

I have in ViewController code
var location = CLLocation()
DispatchQueue.global().sync {
let objLocationManager = clsLocationManager()
location = objLocationManager.findLocationByAddress(address: self.txtTo.stringValue)
}
lblToLatitude.stringValue = String(location.coordinate.latitude)
lblToLongitude.stringValue = String(location.coordinate.longitude)
calling findLocationByAddress method which is implemented in separate class clsLocationManager like this
func findLocationByAddress(address: String) -> CLLocation {
let geoCoder = CLGeocoder()
var location = CLLocation()
geoCoder.geocodeAddressString(address, completionHandler: {(places, error) in
guard error == nil else { return }
location = places![0].location!
})
return location
}
I try to ensure via DispatchQueue.global().sync that geo-coding is executed before passing coordinates to lblToLatitude and lblToLongitude labels but it doesn't work. Of course I could do geo-coding in ViewController code but I'm wondering how to keep it in separate class.
You need a completion
func findLocationByAddress(address: String,completion:#escaping((CLLocation?) -> ())) {
let geoCoder = CLGeocoder()
geoCoder.geocodeAddressString(address, completionHandler: {(places, error) in
guard error == nil else { completion(nil) ; return }
completion(places![0].location!)
})
}
to call
findLocationByAddress { (location) in
if let location = location {
lblToLatitude.stringValue = String(location.coordinate.latitude)
lblToLongitude.stringValue = String(location.coordinate.longitude)
}
}
Also no need for DispatchQueue.global().sync { as the geocoder runs asynchonously in a background thread

How to get user's state In USA

How can I get the state the user lives in?
My code:
let geoCoder = CLGeocoder()
geoCoder.reverseGeocodeLocation(currentUserLocation) { (placemarks, error) -> Void in
if error != nil {
print("Error getting location: \(error)")
} else {
let placeArray = placemarks as [CLPlacemark]!
var placeMark: CLPlacemark!
placeMark = placeArray?[0]
let result = placeMark.addressDictionary
let cityName = result?["City"] as! String
print(cityName)
}
}
CLPlacemark documentation
var administrativeArea: String?
The state or province associated with the placemark.
print(placeMark.administrativeArea)
No need to access the dictionary.

Swift Geocoder won't geocode address

When I pass an address string into CLGeocoder.geocodeAddressString(), the function never executes. When debugging, it never enters geocodeAddressString() and instead skips over. location1 does contain a valid address.
func routeToLocation(location1: String, location2: String) {
var location1Parsed = CLLocation()
var location2Parsed = CLLocation()
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(location1, completionHandler: {(placemarks, error) -> Void in
if ((error) != nil) {
print("Error")
}
if let placemark = placemarks?.first{
let coordinates: CLLocationCoordinate2D = placemark.location!.coordinate
location1Parsed = CLLocation(latitude: coordinates.latitude, longitude: coordinates.longitude)
print(location1Parsed.coordinate.latitude)
print(location1Parsed.coordinate.longitude)
}
})
...
}
Does anyone know why this might be happening?
Thanks

Swift Completion Handler for Reverse Geocoding

I have been banging my head in order to figure out how to fix this piece of code. Basically I have a piece of code that takes a string cityName and stores the latitude and longitude of it in a global variable and call it in another function right after. Apparently because of asynchronous call, I am not able to do that and the value of longitude and latitude are nil.
func findCityCoordinates(cityName: String) {
var geocoder = CLGeocoder()
geocoder.geocodeAddressString(cityName, completionHandler: {(placemarks: [AnyObject]!, error: NSError!) -> Void in
if let placemark = placemarks?[0] as? CLPlacemark {
self.cityLatitude = placemark.location.coordinate.latitude //Returns nil
self.cityLongitude = placemark.location.coordinate.longitude //Returns nil
}
})
}
I have also been trying to work around completion handler but I have no idea on how to implement it and call it in a function. I would appreciate some help.
I was able to use the dispatch async for this. I declared two variables above the geocoder, assign them inside, and use them after it completes.
var lat:Float!
var long:Float!
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(cityName, completionHandler: {(placemarks: [AnyObject]!, error: NSError!) -> Void in
if let placemark = placemarks?[0] as? CLPlacemark {
lat = Float(placemark.location.coordinate.latitude)
long = Float(placemark.location.coordinate.longitude)
}
dispatch_async(
dispatch_get_main_queue(), {
self.cityLatitude = lat
self.cityLongitude = long
})
})
...stores the latitude and longitude of it in a global variable and call it in another function right after
I suspect that the other function is using it before the geocoder completion block sets the values. Put the call to the other function to the completion block, if possible.
func findCityCoordinates(cityName: String) {
var geocoder = CLGeocoder()
geocoder.geocodeAddressString(cityName, completionHandler: {(placemarks: [AnyObject]!, error: NSError!) -> Void in
if let placemark = placemarks?[0] as? CLPlacemark {
self.cityLatitude = placemark.location.coordinate.latitude //Returns nil
self.cityLongitude = placemark.location.coordinate.longitude //Returns nil
*** <-- call your function that uses location here --> ***
}
})
}
Well my brother helped me a little bit out on this, basically I wanted to run a completion block in the IBAction of the save button instead inside of the function findCityCoordinates:
func findCityCoordinate(city: String, completionHandler: (coordinate: CLLocationCoordinate2D) -> ()) {
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(city) { (placemarks: [AnyObject]!, error: NSError!) -> () in
       if let placemark = placemarks[0] as? CLPlacemark {
           let coordinate = placemark.location.coordinate
          completionHandler(coordinate: coordinate)
         }
     }
}
And heres the function being called inside the saveButton action outlet:
findCityCoordinates(searchBar.text) { (cityCoordinate: CLLocationCoordinate2D) -> () in
self.queryForTable(cityCoordinate)
// Force reload of table data
self.myTableView.reloadData()
}

reverseGeocode - completionHandling a completionHandler? (Swift)

What is the best way to achieve this? I have a func that gets the reverseGeocode for a given longitude/latitude and it works just fine. But because it is asynchronous by the time it has got the address information I have already executed the lines to commit the address to a database. Do I need to completionHandle the completionHandler or is there some other way. I did try a do/while loop (don't laugh) and that didn't work.
func reverseGeocode() -> Bool {
let geoCoder = CLGeocoder()
let newLocation = CLLocation(latitude: valueLatitude, longitude: valueLongitude)
geoCoder.reverseGeocodeLocation(newLocation, completionHandler: {(placemarks: [AnyObject]!, error: NSError!) in
if error != nil {
println("LocationsMenu - Geocode failed with error: \(error.localizedDescription)")
}
if placemarks.count > 0 {
let myplacemark = placemarks[0] as! CLPlacemark
let addressDictionary = myplacemark.addressDictionary
let address = addressDictionary[kABPersonAddressStreetKey] as! NSString
let city = addressDictionary[kABPersonAddressCityKey] as! NSString
let state = addressDictionary[kABPersonAddressStateKey] as! NSString
let postcode = addressDictionary[kABPersonAddressZIPKey] as! NSString
let country = addressDictionary[kABPersonAddressCountryKey] as! NSString
println("\(address) \(city) \(state) \(postcode) \(country)")
}
self.showMap(placemarks[0] as! CLPlacemark)
})
}
You simply need to move the code that commits your placemark information to the database inside your completion handler. If that code is called outside your reverseGeocode method, you can refactor reverseGeocode to take a block as a parameter. Then invoke that block inside your completion block (presumably inside your if placemarks.count > 0 if block.)