Home > OS >  Why is My Code running this way and is There a better way to solve this issue
Why is My Code running this way and is There a better way to solve this issue

Time:10-08

// In the code below I am trying to return an array from data in firestore, the array always returned empty when I put the handler outside the for loop so I had to use an if statement inside the for loop to get the array containing the data. after using the print statement you see in the code i found out that the compiler is going over the entire function before entering the for loop, (print("5") & (print("6") are the first to run and when I put the handler outside the for it will also be triggered and return an empty array

**

func getMyGames(joinedGamesIDs: [String], handler: @escaping(_ games: [GameViewModal])  -> ()) {
        var games = [GameViewModal]()
        if !joinedGamesIDs.isEmpty{
            for id in joinedGamesIDs {
                db.collection("games").document(id).getDocument { (document, error) in
                    if let document = document, document.exists {
                        if let game = self.getGameViewModal(document: document){
                            games.append(game)
                            print("1")
                            print(games.count)
                        }
                        print("2")
                        print(games.count)
                    }
                    print("3")
                    print(games.count)
                    if games.count == (joinedGamesIDs.count){
                        handler(games)
                    }
                    print("4")
                    print(games.count)
                }
            }
            print("5")
            print(games.count)
        }
        print("6")
        print(games.count)
    }

**

CodePudding user response:

I've embedded my explanations in the code commentary for easier reading. But the problem you have is that you aren't coordinating these async tasks (the getting of each document). You must coordinate them so when the last one finishes, you can "return" the array from the function. This function doesn't technically "return" anything (except Void) but the completion handler, in a way, "returns" the array which is why I put it in quotes. These semantic details matter and it helps to understand everything better.

func getMyGames(joinedGamesIDs: [String], handler: @escaping (_ games: [GameViewModel]) -> ()) {
    guard !joinedGamesIDs.isEmpty else {
        // If there is nothing to do, always consider
        // calling the handler anyway, with an empty
        // array, so the caller isn't left hanging.
        return handler([])
    }
    // Set up a Dispatch Group to coordinate the multiple
    // async tasks. Instatiate outside of the loop.
    let group = DispatchGroup()
    
    var games: [GameViewModel] = []
    
    for id in joinedGamesIDs {
        // Enter the group on each iteration of async work
        // to be performed.
        group.enter()
        
        db.collection("games").document(id).getDocument { (document, error) in
            if let doc = document,
               doc.exists,
               let game = self.getGameViewModal(document: doc) {
                games.append(game)
            } else if let error = error {
                // Always print errors when in development.
                print(error)
            }
            // No matter what happens inside the iteration,
            // whether there was a success in getting the
            // document or a failure, always leave the group.
            group.leave()
        }
    }
    
    // Once all of the calls to enter the group are equalled
    // by the calls to leave the group, this block is called,
    // which is the group's own completion handler. Here is
    // where you ultimately call the function's handler and
    // return the array.
    group.notify(queue: .main) {
        handler(games)
    }
}
  • Related