Search code examples
swiftfirebasefirebase-realtime-databasecollectionview

Swift Firebase: loop through data and only start the new loop if the old one has finished loading


I'm having trouble because I am loading a very big number of data entries (about 1000) in the form of users. Now, I fetch the users userIDs and insert their rows with the user data into my table view. The thing is, that currently my app waits until it has finished going through all the userIDs there are and only then starts inserting them. Here's the code:

let group = DispatchGroup()

                for each in userIDs {

                    check(identifier: "DispatchGroup", text: "\(each) started")

                    Database.database().reference().child("user-data").child(each).observeSingleEvent(of: .value, with: { (snap) in

                        if let data = snap.value as? [String:AnyObject] {

                            if let name = data["name"] as? String, let urlToImage = data["imagePath"] as? String, let status = data["status"] as? String {

                                let newUser = User()
                                newUser.name = name
                                newUser.imagePath = urlToImage
                                newUser.status = status

                                self.usersTableView.append()

                                if let index = self.usersTableView.index(of: newUser) {
                                    self.tableView.insertItems(at: [IndexPath(row: index, section: 0)])
                                    check(identifier: "FeedLoad", text: "inserting user \(newUser.userName) at timestamp \(Date().timeIntervalSince1970)")
                                }

                                check(identifier: "DispatchGroup", text: "\(each) ended")

                                print("Reloading table view at \(Date().timeIntervalSince1970)")

                                group.leave()

                            }

                        }

                    })



                }

Now, what that prints me out is this:

DispatchGroup: xRDlUIBAsqeI13ykVsEx9P7okph2 started

DispatchGroup: dFVZAQmPb0TRRD94sPR32FbYWyk1 started

DispatchGroup: xRDlUIBAsqeI13ykVsEx9P7okph2 ended

DispatchGroup: dFVZAQmPb0TRRD94sPR32FbYWyk1 ended

But I want it to say:

DispatchGroup: xRDlUIBAsqeI13ykVsEx9P7okph2 started

DispatchGroup: xRDlUIBAsqeI13ykVsEx9P7okph2 ended

DispatchGroup: dFVZAQmPb0TRRD94sPR32FbYWyk1 started

DispatchGroup: dFVZAQmPb0TRRD94sPR32FbYWyk1 ended

How do I achieve this?


Solution

  • There are a couple of ways to achieve this. Firebase has no completion handlers for that sort of stuff, but what you could do is if you have a small amount of IDs in for loop just do is nest it like this:

    Database.database().reference().child("user-data").child(id1).observeSingleEvent(of: .value, with: { (snap) in
        Database.database().reference().child("user-data").child(id2).observeSingleEvent(of: .value, with: { (snap) in
            Database.database().reference().child("user-data").child(id3).observeSingleEvent(of: .value, with: { (snap) in
    
            }
        }
    }
    

    But if that data can vary, the other way of achieving this would be to store a dictionary of ids and Bool to check if finished.

    var ids = ["id1" : false, "id2" : false, "id3" : false]
    
    func getNext() {
        // If all ids == true { return }
        // if not, get the next id
    
        Database.database().reference().child("user-data").child(id1).observeSingleEvent(of: .value, with: { (snap) in
            ids["id1"] = true 
            getNext()
        }
    }
    

    And also don't call Database.Database().reference() every request. Instead store it as a variable. Check this for more info : Firebase best practices