Search code examples
jsonswiftrequestalamofire

Alamofire synchronous request with a loop


I know how to make a synchronous request with completions. But I don't know how to make synchronous requests in a loop.

Here is my code:

    var marks = [JSON]()
    let vnCount = studentVnCodes.count

    var i: Int = 0

    marks = [JSON](repeating: JSON.null, count: vnCount)

    for vn in studentVnCodes {
        let url = "https://example.com/Student/Grade/GetFinalGrades?&vn=\(vn)&academic_year=All"

        Alamofire.request(url).responseString { response in

            var dataString: String = (response.result.value)!

            dataString = cleanMarksJSON(string: dataString)

            if let dict = convertToDictionary(text: dataString) {

                marks[i] = (JSON(dict as Any))

                i += 1

                if (vnCount == marks.count) {
                    completionHandler(marks)
                }
            }

        }
    }

Here I'm trying to make x requests with the number of vn codes (vnCount). The issue is that I get all the JSON in a wrong order in my array of JSON marks. Certainly because it appends responses in the array when it's finished and don't wait the previous request to be ended.

So I tried to create a variable i to force the function to append responses in the right order. That's not working. Any idea? Thanks!


Solution

  • You can run your requests sequentially in a serial queue, in which case they will be executed in the order you call them, which ensures they will be added to the array in order. However, this seems like a suboptimal solution, since you lose execution time by running your requests sequentially instead of concurrently.

    If you still want to implement it like this, see the code below:

    var marks = [JSON]()
    let vnCount = studentVnCodes.count
    marks = [JSON](repeating: JSON.null, count: vnCount)
    let serialQueue = DispatchQueue(label: "serialQueue")
    for vn in studentVnCodes {
        serialQueue.async{
            let url = "https://example.com/Student/Grade/GetFinalGrades?&vn=\(vn)&academic_year=All"
            Alamofire.request(url).responseString { response in
                var dataString: String = (response.result.value)!
                dataString = cleanMarksJSON(string: dataString)
                if let dict = convertToDictionary(text: dataString) {
                    marks.append(JSON(dict as Any))
                    if (vnCount == marks.count) {
                        completionHandler(marks)
                    }
                }
            }
        }
    }
    

    A better solution would be to store the response in a data structure, where ordering doesn't matter, for example in a dictionary, where your keys are the indexes (which you would use for an array) and your values are the JSON response values. This way you can run the requests concurrently and access the responses in order.