forward geocoding completionHandler is not executing in iOS swift4 - ios

I am trying convert to addresses into latlongs to place them on googleMap but, when I pass the address String to geoCoder.geoAddressString(fullAddress) and running application, completionHandler is not executing, I tried with breakpoint at geoCoder.geocodeAddressString(fullAddress) { (placemarks, error) and when I click on stepover it is coming out if the completionHandler..
Now, I have no idea why this is happening... Does anyone know? please Help..
see My code Here:
func loadMarkers() {
for i in 0..<self.professioanlDetailsAH.count {
let fullAddress = "\(self.professioanlDetailsAH[i]["searchAddress"]!)"
if !fullAddress.isEmpty {
var geoCoder = CLGeocoder()
geoCoder.geocodeAddressString(fullAddress) { (placemarks, error) in
guard
let placemarks = placemarks,
let addressLocation = placemarks.first?.location
else {
// handle no location found
return
}
let latitude = addressLocation.coordinate.latitude
let longitude = addressLocation.coordinate.longitude
}
}
}
}
Edit:
func loadMarkers() {
for i in 0..<self.professioanlDetailsAH.count {
print(self.professioanlDetailsAH[i]["firstName"]!)
let fullAddress = "\(self.professioanlDetailsAH[i]["searchAddress"]!)"
print(fullAddress)
if !fullAddress.isEmpty {
geoCoder.geocodeAddressString(fullAddress) {(placemarks, error) in
if error != nil {
print(error!)
}
else{
guard
let placemarks = placemarks,
let addressLocation = placemarks.first?.location
else {return}
let latitude = addressLocation.coordinate.latitude
let longitude = addressLocation.coordinate.longitude
print("lat:\(latitude), long:\(longitude)")
}
}
}
}

Related

How to return a value from CLGeocoder?

I'm working on the weather app as a training and there's a need to convert location to city. So I'm using CLGeocoder like so:
func updateWeatherData(json: JSON?) {
if let json = json {
weatherData.temperature = fahrenheitToCelcius(json["currently"]["temperature"].doubleValue)
weatherData.weatherIconName = json["currently"]["icon"].stringValue
let location = CLLocation(latitude: json["latitude"].doubleValue, longitude: json["longitude"].doubleValue)
CLGeocoder().reverseGeocodeLocation(location) { (placemark, error) in
if let city = placemark {
self.weatherData.city = city.last?.locality
} else if let error = error {
print(error)
}
}
updateUIWithWeatherData()
}
}
//MARK: - UI Updates
func updateUIWithWeatherData() {
cityLabel.text = weatherData.city
temperatureLabel.text = "\(weatherData.temperature)°"
weatherIcon.image = UIImage(named: weatherData.weatherIconName!)
}
And this code returns nil to weatherData.city. But when I place a breakpoint inside a closure, everything works fine. What am I missing?
If I understand your issue correctly you need to update your UI after the geocoding is complete. Like the following:
func updateWeatherData(json: JSON?) {
if let json = json {
weatherData.temperature = fahrenheitToCelcius(json["currently"]["temperature"].doubleValue)
weatherData.weatherIconName = json["currently"]["icon"].stringValue
let location = CLLocation(latitude: json["latitude"].doubleValue, longitude: json["longitude"].doubleValue)
CLGeocoder().reverseGeocodeLocation(location) { (placemark, error) in
if let city = placemark {
self.weatherData.city = city.last?.locality
} else if let error = error {
print(error)
}
self.updateUIWithWeatherData()
}
}
}
The order of these operations is so that the geocoding is done asynchronously and may occur later then the code called after it. Note may but does not need to.
You should also read documentation of this method about threading. UI must be updated on main thread so unless the documentation specifies that the call will be done on main thread you are best forcing it:
CLGeocoder().reverseGeocodeLocation(location) { (placemark, error) in
if let city = placemark {
self.weatherData.city = city.last?.locality
} else if let error = error {
print(error)
}
DispatchQueue.main.async {
self.updateUIWithWeatherData()
}
}

Convert address to coordinates swift

How can I convert a String address to CLLocation coordinates with Swift?
I have no code yet; I looked for a solution but couldn't find any.
Use CLGeocoder to reverse geocode the address into latitude/longitude coordinates:
let address = "1 Infinite Loop, Cupertino, CA 95014"
let geoCoder = CLGeocoder()
geoCoder.geocodeAddressString(address) { (placemarks, error) in
guard
let placemarks = placemarks,
let location = placemarks.first?.location
else {
// handle no location found
return
}
// Use your location
}
You will also need to add and import CoreLocation framework.
You can use CLGeocoder, you can convert address(string) to coordinate and you vice versa, try this:
import CoreLocation
var geocoder = CLGeocoder()
geocoder.geocodeAddressString("your address") {
placemarks, error in
let placemark = placemarks?.first
let lat = placemark?.location?.coordinate.latitude
let lon = placemark?.location?.coordinate.longitude
print("Lat: \(lat), Lon: \(lon)")
}
Here's what I came up with to return a CLLocationCoordinat2D object:
func getLocation(from address: String, completion: #escaping (_ location: CLLocationCoordinate2D?)-> Void) {
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(address) { (placemarks, error) in
guard let placemarks = placemarks,
let location = placemarks.first?.location?.coordinate else {
completion(nil)
return
}
completion(location)
}
}
So let's say I've got this address:
let address = "Springfield, Illinois"
Usage
getLocation(from: address) { location in
print("Location is", location.debugDescription)
// Location is Optional(__C.CLLocationCoordinate2D(latitude: 39.799372, longitude: -89.644458))
}
Swift 5 and Swift 5.1
import CoreLocation
var geocoder = CLGeocoder()
geocoder.geocodeAddressString("your address") { placemarks, error in
let placemark = placemarks?.first
let lat = placemark?.location?.coordinate.latitude
let lon = placemark?.location?.coordinate.longitude
print("Lat: \(lat), Lon: \(lon)")
}
This works
let geocoder = CLGeocoder()
let address = "8787 Snouffer School Rd, Montgomery Village, MD 20879"
geocoder.geocodeAddressString(address, completionHandler: {(placemarks, error) -> Void in
if((error) != nil){
print("Error", error ?? "")
}
if let placemark = placemarks?.first {
let coordinates:CLLocationCoordinate2D = placemark.location!.coordinate
print("Lat: \(coordinates.latitude) -- Long: \(coordinates.longitude)")
}
})
The CLLocationManager object reports locations as a latitude/longitude pair. While these values uniquely represent any location on the planet, they are not values that users immediately associate with the location. Users are more familiar with names that describe a location, such as street names or city names. The CLGeocoder class lets you convert between geographic coordinates and the user-friendly names associated with that location. You can convert from either a latitude/longitude pair to a user friendly place name, or the other way around.
func getCoordinate( addressString : String,
completionHandler: #escaping(CLLocationCoordinate2D, NSError?) -> Void ) {
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(addressString) { (placemarks, error) in
if error == nil {
if let placemark = placemarks?[0] {
let location = placemark.location!
completionHandler(location.coordinate, nil)
return
}
}
completionHandler(kCLLocationCoordinate2DInvalid, error as NSError?)
}
}

CLGeocoder error. GEOErrorDomain Code=-3

When I tried to use reverse geocoding,this error message showed up.
Geocode error: Error Domain=GEOErrorDomain Code=-3 "(null)"
My code is below:
import CoreLocation
geocoder.reverseGeocodeLocation(location) { (placemarks, error) in
if let placemarks = placemarks {
reverseGeocodeLocations[hash] = placemarks
}
callback(placemarks, error)
}
This works only time to time, and I request reverseGeocode several times per seconds. So I guess this error message is related to the limit of request or something?
Is there any documentation about apple’s geocode request?
Thanks for advance.
Updated
here is my entire code for requesting
import CoreLocation
fileprivate struct ReverseGeocodeRequest {
private static let geocoder = CLGeocoder()
private static var reverseGeocodeLocations = [Int: [CLPlacemark]]()
private static let reverseGeocodeQueue = DispatchQueue(label: "ReverseGeocodeRequest.reverseGeocodeQueue")
private static var nextPriority: UInt = 0
fileprivate static func request(location: CLLocation, callback: #escaping ([CLPlacemark]?, Error?)->Void) {
let hash = location.hash
if let value = reverseGeocodeLocations[hash] {
callback(value, nil)
} else {
reverseGeocodeQueue.async {
guard let value = reverseGeocodeLocations[hash] else {
geocoder.reverseGeocodeLocation(location) { (placemarks, error) in
if let placemarks = placemarks {
reverseGeocodeLocations[hash] = placemarks
}
callback(placemarks, error)
}
return
}
callback(value, nil)
}
}
}
let priority: UInt
let location: CLLocation
let handler : ([CLPlacemark]?, Error?)->Void
private init (location: CLLocation, handler: #escaping ([CLPlacemark]?, Error?)->Void) {
ReverseGeocodeRequest.nextPriority += 1
self.priority = ReverseGeocodeRequest.nextPriority
self.location = location
self.handler = handler
}
}
extension ReverseGeocodeRequest: Comparable {
static fileprivate func < (lhs: ReverseGeocodeRequest, rhs: ReverseGeocodeRequest) -> Bool {
return lhs.priority < rhs.priority
}
static fileprivate func == (lhs: ReverseGeocodeRequest, rhs: ReverseGeocodeRequest) -> Bool {
return lhs.priority == rhs.priority
}
}
extension CLLocation {
func reverseGeocodeLocation(callback: #escaping ([CLPlacemark]?, Error?)->Void) {
ReverseGeocodeRequest.request(location: self, callback: callback)
}
func getPlaceName(callback: #escaping (Error?, String?)->Void) {
self.reverseGeocodeLocation { (placemarks, error) in
guard let placemarks = placemarks, error == nil else {
callback(error, nil)
return
}
guard let placemark = placemarks.first else {
callback(nil, "Mysterious place")
return
}
if let areaOfInterest = placemark.areasOfInterest?.first {
callback(nil, areaOfInterest)
} else if let locality = placemark.locality {
callback(nil, locality)
} else {
callback(nil, "On the Earth")
}
}
}
}
After searching everywhere for the answer it was in Apples docs! :/
https://developer.apple.com/reference/corelocation/clgeocoder/1423621-reversegeocodelocation
Geocoding requests are rate-limited for each app, so making too many requests in a short period of time may cause some of the requests to fail. When the maximum rate is exceeded, the geocoder passes an error object with the value network to your completion handler.
When checking the error code in the completion handler it is indeed Network Error:2
Hope this helps someone!

Swift: Asynchronous call in a for loop

I am trying to do the following in swift - Trying to reverse decode a list of addresses in an array and print their latitude/longitude coordinates. The code I have is as follows.
let addressArray = ["Address 1", "Address 2"]
var coordinatesArray = [CLLocationCoordinate2D]()
override func viewDidLoad() {
super.viewDidLoad()
createAddressList()
printAddressList()
}
func printAddressList() {
for i in 0 ..< addressArray.count {
print("Address = \(addressArray[i]) Coordinates = \(coordinatesArray[i].latitude),\(coordinatesArray[i].latitude)")
}
func createAddressList() {
for i in 0 ..< addressArray.count {
let address = addressArray[i]
geocoder.geocodeAddressString(address, completionHandler: {(placemarks, error) -> Void in
print("Address = \(address)");
if let placemark = placemarks?.first {
let coordinate = placemark.location?.coordinate
self.coordinatesArray.append(coordinate!)
}
})
}
}
}
The code prints only the first address that's decoded and nothing happens then.
I do have a fix for this like the below one, which is to move the printAddressList call from viewDidLoad method like this
func createAddressList() {
if count < self.addressArray.count {
let address = addressArray[count]
geocoder.geocodeAddressString(address, completionHandler: {(placemarks, error) -> Void in
print("Address = \(address)");
if let placemark = placemarks?.first {
let coordinate = placemark.location?.coordinate
self.coordinatesArray.append(coordinate!)
}
print("Count = \(self.count)")
self.count += 1
self.createAddressList()
})
} else {
printAddressList()
}
}
Even though the latter solution works, I see that it's not clean, would like to know the right way to do this while making the code readable and clean.
How about using this structure?
let workGroup = dispatch_group_create()
for i in 0..<addressArray.count {
dispatch_group_enter(workGroup)
performGeoCoding({ successCallback :
dispatch_group_leave(workGroup)
})
}
dispatch_group_notify(workGroup, dispatch_get_main_queue()){
successCallback()
printAddressList()
}
There is very nice tutorial about dispatch_group here.
A bit more updated would be something like:
let dispatchGroup = DispatchGroup()
for address in addressArray {
dispatchGroup.enter()
performGeoCoding { address in
dispatchGroup.leave()
}
}
dispatchGroup.notify(queue: .main) {
completionHandler()
}

CLGeocoder reverseGeocodeLocation Energy leak issues

I am using a CLGeocoder().reverseGeocodeLocation and when it is ran, I get "very high" energy consumption and a bit of Overhead. Here's my code:
if CLLocationManager.authorizationStatus() == .AuthorizedWhenInUse {
var currentLatCoord = Double()
if manager.location?.coordinate.latitude != nil {
currentLatCoord = (manager.location?.coordinate.latitude)!
} else {
currentLatCoord = 0.0
print("oops!")
}
var currentLongCoord = Double()
if manager.location?.coordinate.longitude != nil {
currentLongCoord = (manager.location?.coordinate.longitude)!
} else {
currentLongCoord = 0.0
print("oops!")
}
CLGeocoder().reverseGeocodeLocation(CLLocation(latitude: currentLatCoord, longitude: currentLongCoord)) { (placemarks, error) -> Void in
if error != nil {
print("oops!")
print(error)
return
}
let placeArray = placemarks as [CLPlacemark]!
var placeMark: CLPlacemark
placeMark = placeArray![0]
if let thoroughfare = placeMark.addressDictionary?["Thoroughfare"] as? String {
self.locationLabel.text = thoroughfare
} else {
self.locationLabel.text = "Error!"
print("oops!")
}
}
}
Here's the debugger:
If anyone could find out why, and I searched all over for the reasoning behind this and couldn't find it, please let me know!

Resources