【问题标题】:Firebase Firestore Pagination with Swift使用 Swift 进行 Firebase Firestore 分页
【发布时间】:2018-04-23 12:49:35
【问题描述】:

在我的应用中,我尝试使用以下代码从 Firestore 对我的数据(每页 10 个帖子)进行分页,

import UIKit
import FirebaseFirestore
class Home: UITableViewController {


    var postArray = [postObject]()
    let db = Firestore.firestore()
    var page : DocumentSnapshot? = nil
    let pagingSpinner = UIActivityIndicatorView(activityIndicatorStyle: .gray)

    override func viewDidLoad() {
        super.viewDidLoad()

       loadFirstPage()
    }


    func loadFirstPage(){

        // Get the first 10 posts

        db.collection("POSTS").limit(to: 10).addSnapshotListener { (snapshot, error) in
            if snapshot != nil {
                self.postArray = (snapshot?.documents.flatMap({postObject(dec : $0.data())}))!

                // Save the last Document

                self.page = snapshot?.documents.last
                self.tableView.reloadData()
            }
        }
    }

    func loadNextPage(){

       // get the next 10 posts

        db.collection("POSTS").limit(to: 10).start(afterDocument: page!).addSnapshotListener { (snapshot, error) in
            if snapshot != nil {

                for doc in (snapshot?.documents)! {

                    self.postArray.append(postObject(dec: doc.data()))
                }

                self.page = snapshot?.documents.last

                self.tableView.reloadData()

            }

        }

    }


    override func numberOfSections(in tableView: UITableView) -> Int {

        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return postArray.count
    }


    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as? postCell

        // display data

        cell?.textLabel?.text = postArray[indexPath.row].name

        return cell!
    }


    override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {

        // check index to load next page

        if indexPath.row < (self.postArray.count){


            pagingSpinner.startAnimating()
            pagingSpinner.color = UIColor.red
            pagingSpinner.hidesWhenStopped = true
            tableView.tableFooterView = pagingSpinner
            loadNextPage()


        }

    }


}

但我遇到了以下问题:

  • 如果我第一次开始发布内容(FireStore 没有 数据)来自其他设备的应用程序将崩溃,因为 page 将始终为 nil
  • 我尝试通过控制台插入 10 个帖子并在我检查应用程序时 开始使用我的表格视图向下滚动它会崩溃相同 原因 pagenil

我想知道为什么会发生这种情况,尽管我将最后一个 Sanpshot 文档保存为分页光标!有没有更好的为什么用 Swift 实现分页

【问题讨论】:

  • 我最初的印象可能是您遇到了竞争情况,您的应用可能会在完成获取第一个快照之前尝试调用“loadNextPage”(因此页面将为 nil)。尝试添加一些控制台日志以查看是否是这种情况。
  • @ToddKerpelman 检查问题后,正如您提到的那样,滚动带有条件 if indexPath.row &lt; (self.postArray.count) 的 uitableview 将始终在获取第一个快照之前触发“loadNextPage”

标签: ios swift firebase pagination google-cloud-firestore


【解决方案1】:

如果我们手动(使用getDocuments)而不是自动(使用addSnapshotListener)获取文档,使用Swift 在Firestore 中分页非常简单。

为了可读性,我认为将数据的加载(第一页)与数据的继续(附加页面)分开是明智的。 firestoreQuery 是一个 Query 对象,显然您必须自己构建。

class SomeViewController: UIViewController {
    private var cursor: DocumentSnapshot?
    private let pageSize = 10 // use this for the document-limit value in the query
    private var dataMayContinue = true
    
    /* This method grabs the first page of documents. */
    private func loadData() {
        firestoreQuery.getDocuments(completion: { (snapshot, error) in
            ...
            /* At some point after you've unwrapped the snapshot,
               manage the cursor. */
            if snapshot.count < pageSize {
                /* This return had less than 10 documents, therefore
                   there are no more possible documents to fetch and
                   thus there is no cursor. */
                self.cursor = nil
            } else {
                /* This return had at least 10 documents, therefore
                   there may be more documents to fetch which makes
                   the last document in this snapshot the cursor. */
                self.cursor = snapshot.documents.last
            }
            ...
        })
    }
    
    /* This method continues to paginate documents. */
    private func continueData() {
        guard dataMayContinue,
              let cursor = cursor else {
            return
        }
        dataMayContinue = false /* Because scrolling to bottom will cause this method to be called
                                   in rapid succession, use a boolean flag to limit this method
                                   to one call. */

        firestoreQuery.start(afterDocument: cursor).getDocuments(completion: { (snapshot, error) in
            ...
            /* Always update the cursor whenever Firestore returns
             whether it's loading data or continuing data. */
            if snapshot.count < self.pageSize {
                self.cursor = nil
            } else {
                self.cursor = snapshot.documents.last
            }
            ...
            /* Whenever we exit this method, reset dataMayContinue to true. */
        })
    }
}

/* Let's assume you paginate with infinite scroll which means continuing data
   when the user scrolls to the bottom of the table or collection view. */
extension SomeViewController {
    /* Standard scroll-view delegate */
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        let contentSize = scrollView.contentSize.height
        
        if contentSize - scrollView.contentOffset.y <= scrollView.bounds.height {
            didScrollToBottom()
        }
    }
    
    private func didScrollToBottom() {
        continueData()
    }
}

我们仍然可以使用快照侦听器进行分页,但它需要更多步骤。这是因为当快照侦听器返回时,它将返回单页文档,如果用户已分页浏览多个页面,则更新会将用户重置为单页。补救措施是跟踪在屏幕上呈现了多少页面,并在刷新 UI 之前快照侦听器返回时在用户下方加载这些页面。但是,除此之外还有其他步骤,例如在 UI 刷新期间处理新的快照返回;这种竞争条件需要严格的序列化,这可能需要也可能不需要信号量或其他有效的东西。

【讨论】:

    猜你喜欢
    • 2019-02-11
    • 2020-08-25
    • 2017-01-01
    • 1970-01-01
    • 2021-12-10
    • 1970-01-01
    • 2021-03-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多