使用查询游标(分页)从Firestore填充UITableView

时间:2019-01-20 08:20:47

标签: swift firebase uitableview google-cloud-firestore

我有一个从Firestore集合填充的UITableView子类。我一次只抓取20个文档,并且当用户接近已加载数组的末尾时,使用UITableViewDataSourcePrefetching委托从Firestore获取20个文档的下一个“页面”。

以下是我的代码,几乎可以完美地实现此目标,并在适当的地方省略/混淆。

class MyCustomViewController: UIViewController {
    @IBOutlet weak var myCustomTableView: MyCustomTableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        myCustomTableView.query = Firestore.firestore().collection("MyCollection").whereField("foo", isEqualTo: "bar").order(by: "timestamp", descending: true)
        myCustomTableView.fetchNextPage()
    }
}

extension MyCustomViewController: UITableViewDataSourcePrefetching {
    func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
        guard let tableView = tableView as? MyCustomTableView else {
            return
        }
        tableView.prefetch(indexPaths: indexPaths)
    }
}

class MyCustomTableView: UITableView {

    var documents = [DocumentSnapshot]()
    var query: Query?
    let querySize = 20

    private var fetchSemaphore = DispatchSemaphore(value: 1)

    public func prefetch(indexPaths: [IndexPath]) {
        for indexPath in indexPaths {
            if indexPath.section >= documents.count - 1 {
                fetchNextPage()
                return
            }
        }
    }

    public func fetchNextPage() {
        guard let query = query else {
            return
        }
        guard documents.count % querySize == 0 else {
            print("No further pages to fetch.")
            return
        }
        guard fetchSemaphore.wait(timeout: .now()) == .success else { return }

        if self.documents.isEmpty {
            query.limit(to: querySize).addSnapshotListener { (snapshot, error) in
                guard let snapshot = snapshot else {
                    return
                }
                self.documents.append(contentsOf: snapshot.documents)
                self.reloadData()
                self.fetchSemaphore.signal()
            }
        }
        else {
            // I think the problem is on this next line
            query.limit(to: querySize).start(afterDocument: documents.last!).addSnapshotListener { (snapshot, error) in
                guard let snapshot = snapshot else {
                    return
                }

                for document in snapshot.documents {
                    if let index = self.documents.firstIndex(where: { $0.documentID == document.documentID }) {
                        self.documents[index] = document
                    }
                    else {
                        self.documents.append(document)
                    }
                }

                self.reloadData()
                self.fetchSemaphore.signal()
            }
        }
    }
}

我想我知道问题出在哪里/在哪里(请参阅fetchNextPage()中的注释),但我可能是错的。即使我是正确的,我也无法提出解决方案。

我的查询按文档的timestamp值以降序排序,顾名思义,该值代表创建文档的时间。这意味着在表格视图中,最新文档将显示在顶部,而最旧文档显示在底部。

除...外,一切正常。

问题:创建新文档时,表中的每个项目都将被逐行缩小,因为新文档具有最新的时间戳并将其放置在顶部。很好,除了发生这种情况之外,所有查询快照侦听器(第一个侦听器除外)都不再使用正确的文档开始。最初使用documents.last!检索的文档快照不再是查询应开始的正确文档。我不知道如何更改现有快照侦听器的startAfter参数。我找不到任何可用于更改此查询的方法。

同样,这实际上可能并不是我在添加新文档时看到乱序的原因,但我认为是这样。如果有人对如何解决此问题提出建议,我将不胜感激。

我以前到过这里的参考文献:

其他说明:

  • 即使更容易解决此问题,我也不想在查询中使用getDocuments。在这种情况下,进行近实时更新很重要。
  • 我无法使用FirebaseUI,因为用例需要的功能尚不可用。例如,您可能已经注意到我使用indexPath.section而不是indexPath.row,这是因为表由许多单行节组成,因此可以在单元格之间放置垂直填充。

0 个答案:

没有答案