Swift iOS:Firebase 分页

Swift iOS: Firebase Paging

我有这个 Firebase 数据:

我想通过分页查询posts条数据。目前我的代码正在将此 JS 代码转换为 Swift 代码

let postsRef = self.rootDatabaseReference.child("development/posts")
postsRef.queryOrderedByChild("createdAt").queryStartingAtValue((page - 1) * count).queryLimitedToFirst(UInt(count)).observeSingleEventOfType(.Value, withBlock: { snapshot in
....

  })

访问时,此数据page: 1, count: 1。我可以获得“posts.a”的数据,但是当我尝试访问 page: 2, count: 1 时,returns 仍然是“posts.a”

我在这里错过了什么?

假设您在将数据推送到 Firebase 时正在或将要使用 childByAutoId(),您可以使用 queryOrderedByKey() 按时间顺序排列数据。文档 here.

The unique key is based on a timestamp, so list items will automatically be ordered chronologically.

要从特定键开始,您必须在查询后附加 queryStartingAtValue(_:)

示例用法:

var count = numberOfItemsPerPage

var query ref.queryOrderedByKey()

if startKey != nil {
  query = query.queryStartingAtValue(startKey)
  count += 1
}

query.queryLimitedToFirst(UInt(count)).observeSingleEventOfType(.Value, withBlock: { snapshot in
  guard var children = snapshot.children.allObjects as? [FIRDataSnapshot] else {
    // Handle error
    return
  }

  if startKey != nil && !children.isEmpty {
    children.removeFirst()
  }

  // Do something with children
})

我知道我来晚了,timominous, but I'd like to share the way I've solved this. This is a full example, it isn't only about pagination. This example is in Swift 4 and I've used a nice library named CodableFirebase (you can find it here) 给出了一个很好的答案来解码 Firebase 快照值。

除此之外,请记住在创建 post 并将该密钥存储在 postId(或您的变量)中时使用 childByAutoId。所以,我们可以稍后使用它。

现在,模型看起来像这样...

    class FeedsModel: Decodable {
        var postId: String!
        var authorId: String! //The author of the post
        var timestamp: Double = 0.0 //We'll use it sort the posts.
        //And other properties like 'likesCount', 'postDescription'...
    }

我们将使用此功能最近时尚中的posts

    class func getFeedsWith(lastKey: String?, completion: @escaping ((Bool, [FeedsModel]?) -> Void)) {
        let feedsReference = Database.database().reference().child("YOUR FEEDS' NODE")
        let query = (lastKey != nil) ? feedsReference.queryOrderedByKey().queryLimited(toLast: "YOUR NUMBER OF FEEDS PER PAGE" + 1).queryEnding(atValue: lastKey): feedsReference.queryOrderedByKey().queryLimited(toLast: "YOUR NUMBER OF FEEDS PER PAGE")
        //Last key would be nil initially(for the first page).

        query.observeSingleEvent(of: .value) { (snapshot) in
            guard snapshot.exists(), let value = snapshot.value else {
                completion(false, nil)
                return
            }
            do {
                let model = try FirebaseDecoder().decode([String: FeedsModel].self, from: value)
                //We get the feeds in ['childAddedByAutoId key': model] manner. CodableFirebase decodes the data and we get our models populated.
                var feeds = model.map { [=11=].value }
                //Leaving the keys aside to get the array [FeedsModel]
                feeds.sort(by: { (P, Q) -> Bool in P.timestamp > Q.timestamp }) 
                //Sorting the values based on the timestamp, following recent first fashion. It is required because we may have lost the chronological order in the last steps.
                if lastKey != nil { feeds = Array(feeds.dropFirst()) }
                //Need to remove the first element(Only when the lastKey was not nil) because, it would be the same as the last one in the previous page.
                completion(true, feeds)
                //We get our data sorted and ready here.
            } catch let error {
                print("Error occured while decoding - \(error.localizedDescription)")
                completion(false, nil)
            }
        }
    }

现在,在我们的 viewController 中,对于初始加载,函数调用在 viewDidLoad 中是这样进行的。当 tableView 将显示单元格时,将获取下一页...

    class FeedsViewController: UIViewController {

        //MARK: - Properties
        @IBOutlet weak var feedsTableView: UITableView!

        var dataArray = [FeedsModel]()
        var isFetching = Bool()
        var previousKey = String()
        var hasFetchedLastPage = Bool()


        //MARK: - ViewController LifeCycle
        override func viewDidLoad() {
            super.viewDidLoad()
            //Any other stuffs..
            self.getFeedsWith(lastKey: nil) //Initial load.
        }

        //....

        func getFeedsWith(lastKey: String?) {

            guard !self.isFetching else {
                self.previousKey = ""
                return
            }
            self.isFetching = true

            FeedsModel.getFeedsWith(lastKey: lastKey) { (status, data) in
                self.isFetching = false
                guard status, let feeds = data else {
                    //Handle errors
                    return
                }

                if self.dataArray.isEmpty { //It'd be, when it's the first time.
                    self.dataArray = feeds
                    self.feedsTableView.reloadSections(IndexSet(integer: 0), with: .fade)
                } else {
                    self.hasFetchedLastPage = feeds.count < "YOUR FEEDS PER PAGE"
                    //To make sure if we've fetched the last page and we're in no need to call this function anymore.
                    self.dataArray += feeds
                   //Appending the next page's feed. As we're getting the feeds in the recent first manner.
                    self.feedsTableView.reloadData()
                }
            }
        }

        //MARK: - TableView Delegate & DataSource

        //....

        func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
            if self.dataArray.count - 1 == indexPath.row && !self.hasFetchedLastPage {
            let lastKey = self.dataArray[indexPath.row].postId
            guard lastKey != self.previousKey else { return }
            //Getting the feeds with last element's postId. (postId would be the same as a specific node in YourDatabase/Feeds).
            self.getFeedsWith(lastKey: lastKey)
            self.previousKey = lastKey ?? ""
        }

        //....
    }