Home > Software engineering >  Issue using async/await in loop | Swift
Issue using async/await in loop | Swift

Time:08-15

Essentially I am converting string addresses into CLLocationCoordinate2D, to acheive this I am looping through an array of addresses that are in a specific order I need to keep the same, but when the escaping async function runs it changes the order of the array addresses as a result, how can async/await be used to achieve this result?

Resources like this are helpful but I am still struggling to understand the proper way to implement. https://www.biteinteractive.com/swift-5-5-asynchronous-looping-with-async-await/

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

let addresses = ["SAUGET, IL", "SAN FRANCISCO, CA", "SAINT LOUIS, MO"]

for address in addresses {
    print(address)
    getLocation(from: address) { location in
        
        //printing not the same order as the order the loop is sending them in.
        print("address: \(address)")
    }
}

CodePudding user response:

Just make an rendition that uses the async-await rendition of geocodeAddressString:

func getCoordinate(from address: String) async throws -> CLLocationCoordinate2D {
    let geocoder = CLGeocoder()

    guard let location = try await geocoder.geocodeAddressString(address)
        .compactMap( { $0.location } )
        .first(where: { $0.horizontalAccuracy >= 0 } )
    else {
        throw CLError(.geocodeFoundNoResult)
    }

    return location.coordinate
}

Then you can do:

func lookupCoordinates() async throws {
    let addresses = ["SAUGET, IL", "SAN FRANCISCO, CA", "SAINT LOUIS, MO", "NEW YORK, NY"]
    
    for address in addresses {
        let coordinate = try await getCoordinate(from: address)
        print("address: \(coordinate)")
    }
}

Unrelated, we should note that the geocodeAddressString documentation warns us:

After initiating a forward-geocoding request, do not attempt to initiate another reverse- or forward-geocoding request.

The CLGeocoder documentation also warns us:

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. ... Send at most one geocoding request for any one user action.

Just a FYI.

  • Related