0

The situation is I need to loop through multiple objects and make a network call on each.

After all of the network calls are finished, I will call my completion block with all of the data that I collected from these network calls.

To accomplish this, I am attempting to use a dispatch group, entering when the network call begins and leaving when it is finished:

 for user in users { dispatch_group_enter(downloadGroup) UserManager.retrieveFriendsForUser(user, completed: { (usersFriends, fault) in guard let usersFriends = usersFriends else { return } dispatch_group_leave(downloadGroup) }) } 

I am then waiting for them to finish with the following:

dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER); 

However, it does not seem to wait for all of my grouped network calls to finish.

Here is all of the code in complete context; I am looping through many users and then querying for their friends. And I want the method to return all of their friends combined (by concatenating all the results to the allNonFriends array):

class func retrieveNonFriendsOfFriends(completed : (users : [BackendlessUser]?, fault : Fault?) -> Void) { var allNonFriends = [BackendlessUser]() let downloadGroup = dispatch_group_create() // 2 dispatch_group_enter(downloadGroup) // 3 UserManager.retrieveCurrentUsersFriends { (users, fault) in guard let users = users else { print("Server reported an error: \(fault)") completed(users: nil, fault: fault) return } for user in users { dispatch_group_enter(downloadGroup) // 3 UserManager.retrieveFriendsForUser(user, completed: { (usersFriends, fault) in guard let usersFriends = usersFriends else { print("Server reported an error: \(fault)") return } let nonFriends = usersFriends.arrayWithoutFriends(users) allNonFriends += nonFriends dispatch_group_leave(downloadGroup) // 4 }) } } dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER); completed(users: allNonFriends, fault: nil) } 
1
  • Not a solution, but you don't leave the dispatch group in your guard cases. Have you tried adding print statements to see what order things are happening in? How many results do you get in allNonFriends? Commented Apr 11, 2016 at 22:35

1 Answer 1

2

For each time you call dispatch_group_enter(), you need to call dispatch_group_leave(); if you call dispatch_group_enter() 5 times then you also need to call dispatch_group_leave() 5 times.

You make 1 call for retrieving the current user's friends

dispatch_group_enter(downloadGroup) // 3 UserManager.retrieveCurrentUsersFriends 

and then #users calls once that has completed

for user in users { dispatch_group_enter(downloadGroup) // 3 

Problem 1

You don't balance that first call for retrieving the current user's friends.

Problem 2

if userFriends is nil you never end up calling dispatch_group_leave() for that user.

The end result is that you never complete the dispatch_group and you wait forever.

Solution

dispatch_group_enter(downloadGroup) // 3 UserManager.retrieveCurrentUsersFriends { (users, fault) in defer { // Whether we return early or use the users we want to leave the group // Balances the initial enter() dispatch_group_leave(downloadGroup) } ... process users for user in users { dispatch_group_enter(downloadGroup) // 3 UserManager.retrieveFriendsForUser(user, completed: { (usersFriends, fault) in // No matter the outcome of the call we want to leave the // dispatch_group so we don't wait forever defer { // Balances the enter() for each user dispatch_group_leave(downloadGroup) } .... process userFriends } } 

Recommended reading: http://commandshift.co.uk/blog/2014/03/19/using-dispatch-groups-to-wait-for-multiple-web-services/

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.