Firebase Firestore与Swift分页

Ali*_*dil 5 pagination ios firebase swift google-cloud-firestore

使用我的应用程序,我尝试使用下面的代码从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()


        }

    }


}
Run Code Online (Sandbox Code Playgroud)

但我遇到了以下问题:

  • 如果我第一次发布一些东西(FireStore根本就没有数据)来自其他设备,应用程序将崩溃,因为 页面总是为零.
  • 我试图插入后10由控制台和检查应用程序,当我开始滚动下来我的表视图就会死机出于同样的原因.

我想知道为什么会发生这种情况虽然我将最后一个Sanpshot文档保存为分页光标!是否有更好的原因来实现Swift的分页

bso*_*sod 5

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

我认为为了可读性,将数据加载(第一页)与连续数据(附加页面)分开是明智的。显然你必须自己构建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()
    }
}
Run Code Online (Sandbox Code Playgroud)

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