How to handle closures and completion handlers with async requests?

Hi
I have an array of Strings and for each one of them I need to make an Alamofire request to extract a specific value from the outputted JSON.


let array = ["A", "B","C"]
for variableName in array {
     AF.request("API ENDPOINT \(variableName)").responseJSON { response in
          //Parse the JSON
          //Write a value to a .txt file on disk together with the variableName: i.e (variableName;JSONValue)
     }
}

No problem with that.

Thing is, when I go to write the extracted values to a .txt file in the completion handler, the order of the Array is not observed. I understand that the request is async so it all depends on how much time every single request takes, hence the unordered list. So, how could I keep the .txt file list sorted following the order of the Array?
Edit:
Yes, I could create another array with all the results inside. Then sort it (using some kind of sorting algorithm to reflect the order of the initial array), and then write it to the .txt file. But I was wondering if I somehow can skip this part.


Expected result:

let array = ["A", "B","C"]
Textfile = A;(some value from the JSON request);B;(...);C;(...)

But I get this instead:

Textfile = B;(some value from the JSON request);C;(...);A;(...)

Accepted Reply

But I was wondering if I somehow can skip this part.

You know why such things happen and how to supress such things to happen, why do you skip one sure way to make it?

Does it cause something wrong?


Anyway, the following is very near you described, just without `sort`:

var resultText = "" // Sort of simulating .txt file...

let array = ["A", "B", "C"]
var results: [String] = Array(repeating: "", count: array.count)
let group = DispatchGroup()
for _ in array {
    group.enter()
}
group.notify(queue: .main) {
    resultText = results.joined()
    print(resultText)
}
for (i, variableName) in array.enumerated() {
    AF.request("API ENDPOINT \(variableName)").responseJSON { response in
        //Parse the JSON
        //Write a value to a .txt file on disk together with the variableName: i.e (variableName;JSONValue)
        let txt = "\(variableName);\(response.value!);"
        
        results[i] = txt
        
        group.leave()
    }
}

Always shows:

A;(some value from the JSON request);B;(some value from the JSON request);C;(some value from the JSON request);

Replies

But I was wondering if I somehow can skip this part.

You know why such things happen and how to supress such things to happen, why do you skip one sure way to make it?

Does it cause something wrong?


Anyway, the following is very near you described, just without `sort`:

var resultText = "" // Sort of simulating .txt file...

let array = ["A", "B", "C"]
var results: [String] = Array(repeating: "", count: array.count)
let group = DispatchGroup()
for _ in array {
    group.enter()
}
group.notify(queue: .main) {
    resultText = results.joined()
    print(resultText)
}
for (i, variableName) in array.enumerated() {
    AF.request("API ENDPOINT \(variableName)").responseJSON { response in
        //Parse the JSON
        //Write a value to a .txt file on disk together with the variableName: i.e (variableName;JSONValue)
        let txt = "\(variableName);\(response.value!);"
        
        results[i] = txt
        
        group.leave()
    }
}

Always shows:

A;(some value from the JSON request);B;(some value from the JSON request);C;(some value from the JSON request);

> You know why such things happen and how to supress such things to happen, why do you skip one sure way to make it?

Does it cause something wrong?

I like to complicate things 😁.
Jokes aside, I wanted to make sure I didn't miss anything as far as closures are concerned. I thought there would be something else I could use and you gently pointed it out.
Basically you declared a DispatchGroup() which is going to submit different groups of tasks and track when they are all complete. Right?
Btw, thank you!

which is going to submit different groups of tasks and track when they are all complete. Right?

Right. In the near future, using Combine framework would be our another choice, but as for now it may be too early to adopt as it is available only for iOS 13+/macOS 10.15+.