使用 Swift 进行 Firebase Firestore 分页
Posted
技术标签:
【中文标题】使用 Swift 进行 Firebase Firestore 分页【英文标题】:Firebase Firestore Pagination with Swift 【发布时间】: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 个帖子并在我检查应用程序时 开始使用我的表格视图向下滚动它会崩溃相同 原因 page 为 nil。我想知道为什么会发生这种情况,尽管我将最后一个 Sanpshot 文档保存为分页光标!有没有更好的为什么用 Swift 实现分页
【问题讨论】:
我最初的印象可能是您遇到了竞争情况,您的应用可能会在完成获取第一个快照之前尝试调用“loadNextPage”(因此页面将为 nil)。尝试添加一些控制台日志以查看是否是这种情况。 @ToddKerpelman 检查问题后,正如您提到的那样,滚动带有条件if indexPath.row < (self.postArray.count)
的 uitableview 将始终在获取第一个快照之前触发“loadNextPage”
【参考方案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 刷新期间处理新的快照返回;这种竞争条件需要严格的序列化,这可能需要也可能不需要信号量或其他有效的东西。
【讨论】:
以上是关于使用 Swift 进行 Firebase Firestore 分页的主要内容,如果未能解决你的问题,请参考以下文章
当应用程序关闭时,Rest Api Firebase 通知在 Swift 4 中不起作用
使用@angular/fire@6.0.3 无法访问firebase.firestore.FieldValue.serverTimestamp()
如何修复:“@angular/fire”'没有导出成员 'AngularFireModule'.ts(2305) ionic、firebase、angular
Firebase 存储上传图像不适用于亚马逊火灾(型号:Fire 7)