milo526
milo526

Reputation: 5083

Return from asynchronous request in for loop

I’m trying to get data from my RestAPI, specifically i’m getting an array of integers (which are id’s from other users), i want to loop through this array and download the data from all the other customers. A simplified version of the code is show below.

func asyncFunc(completion: (something:[Int])->Void){
  //Get a json Array asynchonous from my RestAPI
  let jsonArray = [1,2,3,4,5]

  var resultingArray:[Int] = []

  for myThing in jsonArray{
    anotherAsyncFunc(myThing, completion: { (somethingElse) -> Void in
      resultingArray.append(somethingElse)
    })
  }
}

func anotherAsyncFunc(data:Int, completion: (somethingElse:Int)->Void){
  //Get some more jsonData from RestApi/data
  let myLoadedData:Int = data*13356
  completion(somethingElse: myLoadedData)
}

How would I make my asyncFunc return an array with all the items it has gotten from the second (inner) async request.

I have tried getting the count of the array which is first requested from the Rest Api and just “blocking” the UI thread by using a while loop too see if the “new” array has collected all the data (the count is equal to the count of the first requested array). This has 2 major disadvantages, mainly it blocks the UI thread and further more, it will fail and crash the app if the data connection gets broken while i’m getting the data from the other users (the inner async request), cause the while loop will never complete.

My question is how would I use a completion handler to return all the data that it should return without blocking the main thread and/or having to worry about badly timed data connection losses.

Upvotes: 1

Views: 101

Answers (1)

Rob
Rob

Reputation: 437632

You can use a dispatch group notification. So create a dispatch group, enter the group for each item in the array, exit in the completion handler of the anotherAsyncFunc asynchronous process, and then create a notification that will trigger the final completion closure when all of the dispatch_group_enter calls have been offset by a corresponding dispatch_group_leave call:

func asyncFunc(completion: (something:[Int])->Void){
    //Get a json Array asynchonous from my RestAPI
    let jsonArray = [1,2,3,4,5]

    var resultingArray:[Int] = []

    let group = dispatch_group_create()

    for myThing in jsonArray {
        dispatch_group_enter(group)
        anotherAsyncFunc(myThing) { somethingElse in
            resultingArray.append(somethingElse)
            dispatch_group_leave(group)
        }
    }

    dispatch_group_notify(group, dispatch_get_main_queue()) {
        completion(something: resultingArray)
    }
}

Note, you will want to make sure you synchronize the updates to resultingArray that the anotherAsyncFunc are performing. The easiest way is to make sure that it dispatches its updates back to the main queue (if your REST API doesn't do that already).

func anotherAsyncFunc(data:Int, completion: (somethingElse:Int)->Void){
    //Get some more jsonData from RestApi/data asynchronously
    let myLoadedData:Int = data*13356
    dispatch_async(dispatch_get_main_queue()) {
        completion(somethingElse: myLoadedData)
    }
}

This is just an example. You can use whatever synchronization mechanism you want, but make sure you synchronize updates on resultingArray accordingly.

Upvotes: 2

Related Questions