cancel the async and make it run normally

Hi so my function getLatFromAddress() is asynchronous and i need it run normally.

func getLatFromAddress(withAddress address: String, completionHandler: @escaping (Double) -> Void) {
  let geocoder = CLGeocoder()
  // Use CLGeocoder to convert the address into coordinates
  geocoder.geocodeAddressString(address) { (placemarks, error) in
    // Return early if there was an error
    guard error == nil else {
      return
    }

    // Return early if no placemarks were found
    guard let placemarks = placemarks, !placemarks.isEmpty else {
      return
    }

    // Use the first placemark to obtain the coordinates
    let location = placemarks.first!.location
    print("lat : ",location!.coordinate.latitude)
    completionHandler(location!.coordinate.latitude)
  }
}

how i use it :

lat1 = 0.00
getLat(withAddress: address1) { (result) in
        lat1 = result
      }
print("lat outFunc = ",lat1)

log :

lat outFunc = 0.00
lat : 43.6044242
Accepted Answer

Is it really how yoiu use it ? how i use it :

getLat(withAddress: address1) { (result) in
        lat1 = result
      }

Or is it

getLatFromAddress(withAddress: address1) { (result) in
        lat1 = result
      }

geocodeAddressString is async by nature, AFAIK you cannot change it. https://mhorga.org/2015/09/23/completion-handlers-in-ios.html

So the ways to solve it:

  • do as you have written: do the print (or any other code) in the completion handler.
  • or use async wait pattern (declaring your func async)

Asynchronous is asynchronous. You have to learn how to design and implement your code to take account of that.

That link from @Claude31 is very, very well worth reading. Then some further thoughts on this:

completionHandler: @escaping (Double) -> Void

It’s better to use type CLLocationDegrees rather than Double. They are actually the same (via typealias) but using CLLocationDegrees is much more clear to someone reading the code. But with that said, I’d suggest you change to result type to CLLocationCoordinate2D (lat + long) or even CLLocation (coordinates plus other information). Why? Because...

Your other post mentions you have an equivalent getLongFromAddress method. That’s not a good idea if you actually need both coordinates, since both call geocodeAddressFromString which is async and relatively slow. Instead you should have a single (say) getLocationFromAddress to get both the latitude and longitude together. If in any any specific call you need only one of them, then that’s fine.

So if you changed the completion handler to this...

completionHandler: @escaping (CLLocationCoordinate2D) -> Void

...there’s still a problem: this doesn’t handle any error from the underlying geocodeAddressFromString call. The early return means the caller of your method never finds out if there was an error and can’t do anything about it. Any completion handler like this should report both success (with a result) and failure (with an error). If you follow the style used by geocodeAddressFromString then your completion handler would look like this:

completionHandler: @escaping (CLLocationCoordinate2D?, Error?) -> Void

That’s fine, though there is a newer convention for implementing this sort of “either result or error” completion handler, using the Result type:

completionHandler: @escaping (Result<CLLocationCoordinate2D, Error>) -> Void

And going further, as Claude said you can convert all of this to the modern async / await pattern, but I think that’s a topic for a bit later.

"Your other post mentions you have an equivalent getLongFromAddress method. That’s not a good idea if you actually need both coordinates, since both call geocodeAddressFromString which is async and relatively slow. Instead you should have a single (say) getLocationFromAddress to get both the latitude and longitude together. If in any any specific call you need only one of them, then that’s fine."

thx you @Claude31@Scott for yours respond and just below i make my function get the lat and the long at same time.

i have another question can you help me to make my function to transform into a async await plz or send me a good tuto

func getLatLongFromAddress(withAddress address: String, completionHandler: @escaping (CLLocationDegrees,CLLocationDegrees) -> Void) {
  let geocoder = CLGeocoder()
  // Use CLGeocoder to convert the address into coordinates
  geocoder.geocodeAddressString(address) { (placemarks, error) in
    // Return early if there was an error
    guard error == nil else {
      return
    }

    // Return early if no placemarks were found
    guard let placemarks = placemarks, !placemarks.isEmpty else {
      return
    }

    // Use the first placemark to obtain the coordinates
    let location = placemarks.first!.location
  let lat = location!.coordinate.latitude
    let long = location!.coordinate.longitude
    print("lat : ",lat)
    print("long : ",long)
    completionHandler(lat,long)
  }
}

on my main :

       getLatLongFromAddress(withAddress: address1) { (lat,long) in
        lat1 = lat
        long1 = long
        print("when i call it : lat1 : ",lat1," long1 : ",long1)

      }
      getLatLongFromAddress(withAddress: address2) { (lat,long) in
        lat1 = lat
        long1 = long
        print("when i call it : lat1 : ",lat2," long1 : ",long2)

      }

i have another question can you help me to make my function to transform into a async await plz or send me a good tuto

Sure, if I can.

Please post it in a new thread, referencing this one if useful.

cancel the async and make it run normally
 
 
Q