Search code examples
swiftfirebasefirebase-realtime-databasetableview

Merging multiple Firebase Snapshot Data in the same tableView


In my App I want to show a feed which generates a tableView depending on which groups the user subscribed. In the background two Firebase request are done and then converted to a tableView. My Code shows the content correct. But there are two issues. The order is wrong. It should be 4->3->2->1 (based on timestamp) and right now it is random. Also if I click on a comment the didSeleceRowAt works totally wrong. How can I manage to put the result in the same tableView correctly?

func datenBankAbfrage() {

    ref = Database.database().reference().child("placeID/h77e24d95a5479ed7588")
    ref.queryOrdered(byChild: "userTime").queryLimited(toLast: 10).observe(
        DataEventType.value,
        with: { (snapshot) in

            self.ref = Database.database().reference().child(
                "placeID/vh-b83b6e4475e04e3fbaa647d23b")
            self.ref.queryOrdered(byChild: "userTime").queryLimited(toLast: 10).observe(
                DataEventType.value,
                with: { (snapshot2) in

                    for video in snapshot.children.allObjects as! [DataSnapshot] {
                        let Object1 = video.value as? [String: AnyObject]

                        let userName = Object1?["userName"]
                        let userGroup = Object1?["userGroup"]
                        let userComment = Object1?["userComment"]
                        let userTime = Object1?["userTime"]
                        let userLikes = Object1?["userLikes"]
                        let commentId = Object1?["commentId"]

                        ViewComments.commentIDNew = commentId as! String
                        let video = importComment(
                            userName: userName as! String, userGroup: userGroup as! String,
                            userComment: userComment as! String, userTime: userTime as! Int,
                            userLikes: userLikes as! Int, commentId: commentId as! String)
                        self.table.insert(video, at: 0)
                        // self.table.append(video)
                        self.tableView.reloadData()
                    }

                    for video in snapshot2.children.allObjects as! [DataSnapshot] {
                        let Object2 = video.value as? [String: AnyObject]
                        let userName = Object2?["userName"]
                        let userGroup = Object2?["userGroup"]
                        let userComment = Object2?["userComment"]
                        let userTime = Object2?["userTime"]
                        let userLikes = Object2?["userLikes"]
                        let commentId = Object2?["commentId"]
                        ViewComments.commentIDNew = commentId as! String
                        let video2 = importComment(
                            userName: userName as! String, userGroup: userGroup as! String,
                            userComment: userComment as! String, userTime: userTime as! Int,
                            userLikes: userLikes as! Int, commentId: commentId as! String)
                        self.table.insert(video2, at: 0)
                        // self.table.append(video)
                        self.tableView.reloadData()
                    }
                })
        })
}

class importComment {
    var userName: String?
    var userID: String?
    var userGroup: String?
    var userComment: String?
    var userTime: Int?
    var userLikes: Int?
    var commentId: String?

    init(
        userName: String?, userGroup: String?, userComment: String?, userTime: Int?,
        userLikes: Int?, commentId: String?
    ) {
        self.userName = userName
        self.userGroup = userGroup
        self.userComment = userComment
        self.userTime = userTime
        self.userLikes = userLikes
        self.commentId = commentId
    }

}


Solution

  • No need to fetch data in Loops. As you're calling asynchronous functions in a loop, there is no guarantee of order.

    You should use DispatchGroup. Here is how execution will go:

    1. Create a DispatchGroup instance.
    2. Add SnapShot-1 Fetch request to the Dispatch group.
    3. Now add SnapShot-2 Fetch request to the Dispatch group.
    4. Notify() will only be called after both the tasks has been completed. So you should prepare your data there.
    5. Reload Table after data preparation.

    Here is an example implementation:

    func datenBankAbfrage() {
        let dispatchGroup = DispatchGroup()
        
        dispatchGroup.enter()
        ref = Database.database().reference().child("placeID/h77e24d95a5479ed7588")
        ref.queryOrdered(byChild: "userTime").queryLimited(toLast: 10).observe(
            DataEventType.value,
            with: { (snapshot) in
                
                for video in snapshot.children.allObjects as! [DataSnapshot] {
                    let Object1 = video.value as? [String: AnyObject]
                    
                    let userName = Object1?["userName"]
                    let userGroup = Object1?["userGroup"]
                    let userComment = Object1?["userComment"]
                    let userTime = Object1?["userTime"]
                    let userLikes = Object1?["userLikes"]
                    let commentId = Object1?["commentId"]
                    
                    ViewComments.commentIDNew = commentId as! String
                    let video = importComment(
                        userName: userName as! String, userGroup: userGroup as! String,
                        userComment: userComment as! String, userTime: userTime as! Int,
                        userLikes: userLikes as! Int, commentId: commentId as! String)
                    self.table.append(video)
                }
                dispatchGroup.leave()
            })
        
        
        dispatchGroup.enter()
        self.ref = Database.database().reference().child(
            "placeID/vh-b83b6e4475e04e3fbaa647d23b")
        self.ref.queryOrdered(byChild: "userTime").queryLimited(toLast: 10).observe(
            DataEventType.value,
            with: { (snapshot2) in
                
                for video in snapshot2.children.allObjects as! [DataSnapshot] {
                    let Object2 = video.value as? [String: AnyObject]
                    let userName = Object2?["userName"]
                    let userGroup = Object2?["userGroup"]
                    let userComment = Object2?["userComment"]
                    let userTime = Object2?["userTime"]
                    let userLikes = Object2?["userLikes"]
                    let commentId = Object2?["commentId"]
                    ViewComments.commentIDNew = commentId as! String
                    let video2 = importComment(
                        userName: userName as! String, userGroup: userGroup as! String,
                        userComment: userComment as! String, userTime: userTime as! Int,
                        userLikes: userLikes as! Int, commentId: commentId as! String)
                    self.table.append(video)
                }
                dispatchGroup.leave()
            })
        
        dispatchGroup.notify(queue: .main) {
          // This is invoked when all the tasks in the group is completed.
            self.prepareDatasource()
        }
    }
    
    func prepareDatasource() {
        self.table.sort(by: { $0.userTime ?? 0 > $1.userTime ?? 1}) // Sorting data
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
    }