1

I am working on my pagination feature and so far was able to paginate posts by timestamp. The issue is in the version I use (from a course) that it queries posts depending on its value always starting from a value a bit higher than before which works fine when using timestamp as the numbers are really high and more likely to differ. Now I want to do the pagination stuff with my posts ordered by likeCount. The issue now is that as there are many posts/comments with equal likes the pagination doesnt work properly anymore.

Here Is my code

  func observeAllPostsForTrendByLikeCount(start likeCount: Int? = nil, limit: UInt, completionHandler: @escaping ([(Post, UserModel)]) -> Void) {

    var postQuery = REF_POST.queryOrdered(byChild: "likeCount")
    if let latestPostLikeCount = likeCount, latestPostLikeCount >= 0 {
        postQuery = postQuery.queryStarting(atValue: latestPostLikeCount + 1, childKey: "likeCount").queryLimited(toLast: limit)
    } else {
        postQuery = postQuery.queryLimited(toLast: limit)
    }

    postQuery.observeSingleEvent(of: .value, with: { (snapshot) in
        let items = snapshot.children.allObjects
        let myGroup = DispatchGroup()

        var results: [(post: Post, user: UserModel)] = []
        for (index, item) in (items as! [DataSnapshot]).enumerated() {
            myGroup.enter()
            API.Post.observePost(withId: item.key, completion: { (post) in
                API.User.observeUser(withId: post.userId!, completion: { (user) in
                    results.insert((post, user), at: index)
                    myGroup.leave()
                })
            })
        }
        myGroup.notify(queue: .main) {
            results.sort(by: {$0.0.likeCount! > $1.0.likeCount!})
            completionHandler(results)
        }
    })
}

func loadMoreTrendByLikeCount(start likeCount: Int, limit: UInt, completionHandler: @escaping ([(Post, UserModel)]) -> Void) {


    let postOrderedQuery = REF_POST.queryOrdered(byChild: "likeCount")
    let postLimitedQuery = postOrderedQuery.queryEnding(atValue: likeCount , childKey: "likeCount").queryLimited(toLast: limit)

    postLimitedQuery.observeSingleEvent(of: .value, with: { (snapshot) in
        let items = snapshot.children.allObjects

        let myGroup = DispatchGroup()
        var results = [(post: Post, user: UserModel)]()
        for (index, item) in (items as! [DataSnapshot]).enumerated() {
                myGroup.enter()
                API.Post.observePost(withId: item.key, completion: { (post) in
                    API.User.observeUser(withId: post.userId!, completion: { (user) in

                        results.insert((post, user), at: index)
                        myGroup.leave()
                    })
                })
            }

        myGroup.notify(queue: .main) {
            results.sort(by: {$0.0.likeCount! > $1.0.likeCount! })
            completionHandler(results)
        }
    })
}

This code works fine as long as the posts have different like counts but as I scroll deeper and face multiply posts with 0 likes it doesn't load further.

here is how I fetch the posts

   func loadTrendByLikeCount() {
    isLoadingPost = true
    API.Post.observeAllPostsForTrendByLikeCount(start: posts.first?.likeCount, limit: 3) { (results) in
        if results.count > 0 {
            results.forEach({ (result) in
                if self.segmentedControl.selectedSegmentIndex == 1 {
                    self.posts.append(result.0)
                    self.users.append(result.1)
                }
            })
        }
        self.isLoadingPost = false
        if self.refreshControl.isRefreshing {
            self.refreshControl.endRefreshing()
        }
        self.activityIndicatorView.stopAnimating()
        self.tableView.reloadData()
    }

}

and the scroll method

          if scrollView.contentOffset.y >= scrollView.contentSize.height - self.view.frame.size.height {

 guard !isLoadingPost else {
                    return
                }
                isLoadingPost = true

                guard let lastPostLikeCount = self.posts.last?.likeCount else {
                    isLoadingPost = false
                    return
                }
                self.Indicator.startAnimating()

            API.Post.loadMoreTrendByLikeCount start: lastPostLikeCount, limit: 4) { (results) in
                    if results.count == 0 {
                        self.Indicator.stopAnimating()
                        return
                    }

                    for result in results {
                        if !self.posts.contains(where: {$0.id == result.0.id}) {
                            self.posts.append(result.0)
                            self.users.append(result.1)
                        }
                    }
                    self.tableView.reloadData()
                    self.Indicator.stopAnimating()

                    self.isLoadingPost = false
                }

        }

As I said this works and loads more post as long as not too many posts are at the same likecount. Moreover the loadMore() function ends in an infinity loop...

Thanks in advance

Zash__
  • 293
  • 4
  • 16

1 Answers1

0

So first let me explain what is happening here. When you call loadMoreTrendByLikeCount you provide a starting like count let's say 10 likes in this example and you are asking Firebase to get the next limit number of posts. In this example let's assume the limit is 5 and we have the following posts:

"posts": {

  "key-1" : {
      "likes" : 10,
      "timestamp" : 1514852613107,
      "userID" : "iijhsPlIxbWrdQty5nn9IUAZRY63"
    },
    "key-2" : {
      "likes" : 10,
      "timestamp" : 1515369388635,
      "userID" : "iijhsPlIxbWrdQty5nn9IUAZRY63"
    },
    "key-3" : {
      "likes" : 10,
      "timestamp" : 1515538024957,
      "userID" : "iijhsPlIxbWrdQty5nn9IUAZRY63"
    },
    "key-4" : {
      "likes" : 10,
      "timestamp" : 1515700542687,
      "userID" : "iijhsPlIxbWrdQty5nn9IUAZRY63"
    },
    "key-5" : {
      "likes" : 10,
      "timestamp" : 1515704869452,
      "userID" : "iijhsPlIxbWrdQty5nn9IUAZRY63"
    },
    "key-6" : {
      "likes" : 10,
      "timestamp" : 1515704970865,
      "userID" : "iijhsPlIxbWrdQty5nn9IUAZRY63"
    },
    "key-7" : {
      "likes" : 10,
      "timestamp" : 1515773432428,
      "userID" : "iijhsPlIxbWrdQty5nn9IUAZRY63"
    },
    "key-8" : {
      "likes" : 10,
      "timestamp" : 1515872606809,
      "userID" : "iijhsPlIxbWrdQty5nn9IUAZRY63"
    },
    "key-9" : {
      "likes" : 0,
      "timestamp" : 1515970044560,
      "userID" : "iijhsPlIxbWrdQty5nn9IUAZRY63"
    }
}

Now we perform the following query: loadMoreTrendByLikeCount(start likeCount: 10, limit: 5) We will get the following posts key-8, key-7, key-6, key-5, key-4. When you query again you will get the same 5 posts because the query starts in the same place. (i.e. your limit is not large enough to get all posts with 10 likes, thus the starting position stays the same)

Now, I am not sure how to resolve this issue. I have only ever used pagination with the autoID keys which are unique so this issue cannot occur. The only thing I can think of is to detect that you are querying the same posts again and increase the limit so you get new posts. You'll have to keep increasing this limit until you get past all the posts with the same number of likes. Depending on the number of posts this can take awhile and be very inefficient.

Ideally, Firebase would support querying all posts between say 10-11 likes to avoid this problem, but that isn't possible. It looks like you have to somehow store another value related to the likes that allows you to filter as desired (for example if you had "filterLikes": 10.0, "filterLikes": 10.1, "filterLikes": 10.2, etc) . Take a look at this question it might be helpful.

DoesData
  • 6,594
  • 3
  • 39
  • 62
  • thanks, I though the same but hoped that there is a way to work around this issue as I am a beginner :) thanks a lot for this explanation absolutely makes sense, I just try to fetch like 30 posts in hope that this doesn't happen – Zash__ Mar 08 '18 at 18:52