how can i make my async func wait

so i have already made a function here is it and how it work :

 @IBAction func ValidButton(_ sender: Any) {
if(verif(){
//do some stuff
   }print("it pass throught")
}
verif() -> Bool {
bool = true
//do some stuff and make my bool false

var lat1 = 0.00
      var lat2 = 0.00
      var long1 = 0.00
      var long2 = 0.00
       
// where i call my function
      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
        lat2 = lat
        long2 = long
        print("when i call it : lat1 : ",lat2," long1 : ",long2)
      }
//do some stuff
return bool
}

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)
  }
}

here is the log :

it pass throught
lat : 43.5982309
long : 1.4313821
when i call it : lat1 : 43.5982309 long1 : 1.4313821
lat : 43.6044242
long : 1.4437472
when i call it : lat1 : 43.6044242 long1 : 1.4437472
default
Answered by TheNotDevelopper7 in 741160022

i try it with another way to do it because my task still didn't work so how i did it is :

  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
        lat2 = lat
        long2 = long
        print("when i call it : lat2 : ",lat2," long2 : ",long2)
         
//do my stuff

      }
      }

i totally change my whole code it become ugly but atleast it work im really thanksfull to you @Scott and @Claude31 i learn a lot about Async i didn't know about that

I did not test, but logically you should:

  • declare getLatLongFromAddress async
func getLatLongFromAddress(withAddress address: String, completionHandler: @escaping (CLLocationDegrees,CLLocationDegrees) -> Void) async {
  • call it with await
      await getLatLongFromAddress(withAddress: address1) { (lat,long) in
  • Your code is not very clear, so I don't know if you call in the IBAction.

If so, you have to enclose the whole code inside the IBAction in a Task 

 @IBAction func ValidButton(_ sender: Any) {
       Task { @MainActor in
             // The original IBAction code
       }
}

If you declare a function as async then it should not use a completion handler. That’s the whole point of Swift concurrency: it takes over control of delivering results asynchronously so you don’t have to explicitly. So the proper declaration (with the result type adjusted) would be like this:

func getLatLongFromAddress(_ address: String) async throws -> CLLocationCoordinate2D {

As an async function there’s no completion handler. Instead, the task simply pauses until the function returns a result. And throws is needed because that’s how you report any error from the underlying geocoding operation, which may throw an error.

Then to implement this, it’s easiest to use the async (no completion handler) version of the geocode API. That works like this:

let geocoder = CLGeocoder()
let placemarks = try await geocoder.geocodeAddressString(address)
let location = placemarks[0].location // for now we’ll assume [0] always exists
return location!.coordinate // for now we’ll assume the coordinate always exists

Then to call this function from a button handler, you need to call it from a Task block (because it’s async) and you should also put it in a do / catch block so you can handle any errors that get thrown during the geocoding.

it still don't work i don't know to do now ... my task :

   Task {
        do {
          let coordinates = try await getLatLongFromAddress(withAddress: address1)
          lat1 = coordinates.latitude
          long1 = coordinates.longitude
          print("when i call it : lat1 : ",lat1," long1 : ",long1)
        } catch {
          print(error)
        }
         
        do {
          let coordinates = try await getLatLongFromAddress(withAddress: address2)
          lat2 = coordinates.latitude
          long2 = coordinates.longitude
          print("when i call it : lat1 : ",lat1," long1 : ",long1)
        } catch {
          print(error)
        }
      }

my function :

func getLatLongFromAddress(withAddress address: String) async throws -> CLLocationCoordinate2D {
 let geocoder = CLGeocoder()
   
  let placemarks = try await geocoder.geocodeAddressString(address)
   
   
  let location = placemarks[0].location // for now we’ll assume [0] always exists
   
  let result = location!.coordinate
   
  
  return result // for now we’ll assume the coordinate always exists
}

Could you explain what you get and what you expect, in the logs ?

here is the log :

Selected value 2023-01-02 14:59:30 +0000
 lat1 : 0.0 long1 : 0.0
 lat2 : 0.0 long2 : 0.0
2023-01-02 15:59:31.688751+0100 myApp[8046:225025] [Client] {"msg":"#NullIsland Received a latitude or longitude from getLocationForBundleID that was exactly zero", "latIsZero":0, "lonIsZero":0, "location":'80 F6 1C 0A 03 00 00 00'}
end
it pass through
when i call it : lat1 : 43.5982309 long1 : 1.4313821
2023-01-02 15:59:31.834854+0100 myApp[8046:225025] [Client] {"msg":"#NullIsland Received a latitude or longitude from getLocationForBundleID that was exactly zero", "latIsZero":0, "lonIsZero":0, "location":'80 F6 1C 0A 03 00 00 00'}
when i call it : lat2 : 43.6044242 long2 : 1.4437472

Is it the error log ?

 @IBAction func ValidButton(_ sender: Any) {
    Task { @MainActor in
      if verif() {
//do some stuff
   }print("it pass throught")
}
verif() -> Bool {
bool = true
//do some stuff and make my bool false
   var lat1 = 0.00
      var lat2 = 0.00
      var long1 = 0.00
      var long2 = 0.00
     
      Task {
        do {
          let coordinates = try await getLatLongFromAddress(withAddress: address1)
          lat1 = coordinates.latitude
          long1 = coordinates.longitude
          print("when i call it : lat1 : ",lat1," long1 : ",long1)
        } catch {
          print(error)
        }
         
        do {
          let coordinates = try await getLatLongFromAddress(withAddress: address2)
          lat2 = coordinates.latitude
          long2 = coordinates.longitude
          print("when i call it : lat1 : ",lat1," long1 : ",long1)
        } catch {
          print(error)
        }
      }
      print(" lat1 : ", lat1," long1 : ",long1)
      print(" lat2 : ",lat2," long2 : ",long2)

//do some stuff
return bool
}

//myfunction 
func getLatLongFromAddress(withAddress address: String) async throws -> CLLocationCoordinate2D {
 let geocoder = CLGeocoder()
   
  let placemarks = try await geocoder.geocodeAddressString(address)
   
   
  let location = placemarks[0].location // for now we’ll assume [0] always exists
   
  let result = location!.coordinate
   
  
  return result // for now we’ll assume the coordinate always exists
}

here is the log :

 lat1 : 0.0 long1 : 0.0
 lat2 : 0.0 long2 : 0.0
2023-01-02 16:32:36.503786+0100 myApp[8758:254961] [Client] {"msg":"#NullIsland Received a latitude or longitude from getLocationForBundleID that was exactly zero", "latIsZero":0, "lonIsZero":0, "location":'80 B6 C8 08 03 00 00 00'}
end
it pass through
when i call it : lat1 : 43.5982309 long1 : 1.4313821
2023-01-02 16:32:36.554774+0100 myApp[8758:254659] [Client] {"msg":"#NullIsland Received a latitude or longitude from getLocationForBundleID that was exactly zero", "latIsZero":0, "lonIsZero":0, "location":'80 36 D1 08 03 00 00 00'}
when i call it : lat1 : 43.5982309 long1 : 1.4313821
default

Seems it is edited code. This does not compile:

 @IBAction func ValidButton(_ sender: Any) {
    Task { @MainActor in
      if verif() {
//do some stuff
   }print("it pass throught")
}

Error: Consecutive statements on a line must be separated by ';'

So please show the real code.

   @IBAction func validButton(_ sender: Any) {
    Task { @MainActor in
      if verif() {
  let date = universalDate
        let address = self.addressEditTextClient.text
        let cp = self.cpEditTextClient.text
        let city = self.villeEditTextClient.text
        let accessibilite = self.villeEditTextClient.text
        let type_chantier = self.typeChantierEditTextClient.text
        let email = email
         
        var urlLink = "my link"
         
        //dateFormat
         
        let parameters = [my parameter]
     
        var sParams = ""
         
        for (key,value) in parameters{
          //domain.com?parameter1Key=parameter1Value&parameter2Key=parameter2Value
          sParams += key + "=" + (value) + "&"
          print("\(key),\(value)")
           
        }
        if !sParams.isEmpty{
          sParams = "?" + sParams
           
          if sParams.hasSuffix("&"){
            sParams.removeLast()
             
          }
           
          urlLink = urlLink + sParams
        }else{
          print("!sParams.isEmpty fail")
        }
         
        let serializer = DataResponseSerializer(emptyResponseCodes: [200,204,205])
         
        let urlString = urlLink.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)! //This will fill the spaces with the %20
         
        //API
        let request = URLRequest(url : URL(string:urlString)!)
        AF.request(request).uploadProgress{progress in }.response(responseSerializer: serializer){ [self] response in
          if response.error == nil {
            var responseString: String!
             
            responseString = ""
             
            if response.data != nil {
              //receive data-->responseString and put into dataJson
              responseString = String(bytes:response.data!, encoding: .utf8)
              var dataJson = responseString
              //remove [ ] on the dataJson
              dataJson = dataJson!.replacingOccurrences(of: "[", with: "")
              dataJson = dataJson!.replacingOccurrences(of: "]", with: "")
               
              let jsonObjectData = dataJson!.data(using: .utf8)!
              let data = try? JSONDecoder().decode(
                Data.self,
                from: jsonObjectData
              )
              print(data?.result as Any)
            }else{
              responseString = response.response?.description
            }
            print("response time: \(response.metrics?.taskInterval.duration ?? 0)")
          }
        } 
      }
      else{
        print("it pass through")
      }
    }
  }

here is verif :

  func verif() -> Bool {
    var check = true;
    textBlanc()
     
    var message = "Attention : \n";
     
    if !checkIssetEditText() {
      message += " - il faut remplir tous les champs pour valider";
      check = false;
    } else {
      let address1 = makeAddress(let: self.addressEditTextClient.text!,
                    let: self.cpEditTextClient.text!,
                    let: self.villeEditTextClient.text!)
      let address2 = makeAddress(let: "Toulouse",
                    let: "",
                    let: "")
      var lat1 = 0.00
      var lat2 = 0.00
      var long1 = 0.00
      var long2 = 0.00
     
       
      Task {
        do {
          let coordinates = try await getLatLongFromAddress(withAddress: address1)
          lat1 = coordinates.latitude
          long1 = coordinates.longitude
          print("when i call it : lat1 : ",lat1," long1 : ",long1)
        } catch {
          print(error)
        }
         
        do {
          let coordinates = try await getLatLongFromAddress(withAddress: address2)
          lat2 = coordinates.latitude
          long2 = coordinates.longitude
          print("when i call it : lat1 : ",lat1," long1 : ",long1)
        } catch {
          print(error)
        }
      }
       
   
      
      print(" lat1 : ", lat1," long1 : ",long1)
      print(" lat2 : ",lat2," long2 : ",long2)
       
      if !checkCP(let: self.cpEditTextClient.text!){
         
        cpEditTextClient.textColor = .yellow
         
        message += " - le code postal est incorrect \n"
        check = false
      } else if !checkAddress(let: lat1, let: lat2, let: long1, let: long2, let : 55.0){
         
        addressEditTextClient.textColor = .yellow
        cpEditTextClient.textColor = .yellow
        villeEditTextClient.textColor = .yellow
         
        message += " - les livraisons ne peuvent aller au-delà de 50 km de la ville \n"
        check = false
      }
    }
     
    if !check {
      let alert = messageAlerte(let: message,let:"")
      self.present(alert, animated: true, completion: nil)
    }
    print("end")
    return check
     
  }

and my function and log is still the same i just remove my url and my parameter from my button

In your verif() function, the async geocoding is inside a Task block, but below that block you have code that depends on the result of the geocoding. That won’t work. The code after the task block executes immediately after the task is started, which is before the geocoding inside the task completes.

To make this work, your code that needs the geocoding result (specifically the checkAddress() call) needs to be inside the task block. Then it will run after the geocoding instead of before it. So basically your entire verif() function needs to be async. And since you call verif() from within a Task block, you don’t need another Task block inside verif(). Try removing that and changing the declaration like this:

func verif() async -> Bool {

...and then call it like this:

Task { @MainActor in
    if await verif() {
        // ...handle success...
    }
    else {
        // ...handle failure...
    }
}

Also, note you can optimize the logic a bit. It appears you don’t even need the geocoding result unless the checkCP() test returns false. So it may be helpful to not do the geocoding until you actually need it.

Accepted Answer

i try it with another way to do it because my task still didn't work so how i did it is :

  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
        lat2 = lat
        long2 = long
        print("when i call it : lat2 : ",lat2," long2 : ",long2)
         
//do my stuff

      }
      }

i totally change my whole code it become ugly but atleast it work im really thanksfull to you @Scott and @Claude31 i learn a lot about Async i didn't know about that

how can i make my async func wait
 
 
Q