da1lbi3
da1lbi3

Reputation: 4519

Completion handler problems

I have a method where I want to return results when it is ready. But i'm struggling with 2 completion handlers.

This is the code. The first completion handler is members.observesingleEvent. This returns a number of keys from a database. Then I loop over the keys to obtain new values from another call. I loop over the values and do the other call.

private func getRoomDevices(completionHandler : @escaping (Bool) -> ()){


members.observeSingleEvent(of: .value, with: { snapshot in
    if !snapshot.exists() { return  }

    let postDict = snapshot.value as! [String : AnyObject]

    for list in postDict {
        self.ref.child("groups/" + list.key).observeSingleEvent(of: .value, with: { groups in
            self.rooms.append(FirebaseApi().convertRoomDevices(snap: groups))
            print(groups.debugDescription)


        });
    }

    completionHandler(true)

})

I add everything to the room array. But what happens is that completionHandler(true) is called while the for loop is busy. The completionHandler needs to be called when everything is done.

Upvotes: 1

Views: 173

Answers (1)

David Pasztor
David Pasztor

Reputation: 54785

You have to use a DispatchGroup, to which you add all items in your asynchronous loop and only call the completion handler if all items of the group finished execution.

members.observeSingleEvent(of: .value, with: { snapshot in
    if !snapshot.exists() { return  }

    let postDict = snapshot.value as! [String : AnyObject]
    let group = DispatchGroup()
    for list in postDict {
        group.enter()
        self.ref.child("groups/" + list.key).observeSingleEvent(of: .value, with: { groups in
            self.rooms.append(FirebaseApi().convertRoomDevices(snap: groups))
            print(groups.debugDescription)
            group.leave()
        });
    }
    group.notify(queue: DispatchQueue.main) {
        completion(true)
    }

})

Upvotes: 3

Related Questions