Reputation: 5354
I have this function which fetches documents from a collection. I got this logic form the Firestore documentation but I do not understand the purpose of addSnapshotListener
.
In a real scenario I need to:
First run a first query, get the last document and pass it to the next query and so on right?
So this means that after each query I need to return also the last document?
If this is the case why do I need addSnapshotListener
? I can get lastSnapshot
also from .getDocuments()
class HomeViewModel: ObservableObject, LoadProtocol {
var firestoreService: FirestoreService = FirestoreService()
@Published var items: [Item] = []
let first: Query = Firestore.firestore().collection("items").limit(to: 1)
var next: Query = Firestore.firestore().collection("items").limit(to: 1)
init() {
self.first.addSnapshotListener { (snapshot, error) in
guard let snapshot = snapshot else {
print("Error retrieving cities: \(error.debugDescription)")
return
}
guard let lastSnapshot = snapshot.documents.last else {
// The collection is empty.
return
}
// Construct a new query starting after this document,
// retrieving the next 25 cities.
self.next = self.firestoreService.db.collection("items")
.start(afterDocument: lastSnapshot)
// Use the query for pagination.
}
}
func refresh() {
self.firestoreService.fetchCollection(query: self.next) { (result: Result<[Item], Error>) in
switch result {
case .success(let items):
self.items += items
self.addToCategories()
case .failure(let error):
print(error)
}
}
}
}
Upvotes: 1
Views: 1421
Reputation: 5819
The problem is that you are mixing Basic Cursor Pagination with Query Cursor Pagination on your implementation.
The Basic Cursor Pagination Part of your code (up to the first.addSnapshotListener
) is correct and will execute everytime returning more and more data to your map every time you call the function and resolve it, however the Query Cursor Pagination part will never be called, so you can remove that part completely from your implementation.
So your code should look like:
func fetchCollection<Collection: CollectionProtocol>(lastSnapshot: DocumentSnapshot?, query: Query, completion: @escaping (Result<[Collection], Error>) -> Void) {
var first = query.limit(to: 1)
if let lastSnapshot = lastSnapshot {
first = query.start(afterDocument: lastSnapshot)
}
first.getDocuments() { (querySnapshot, error) in
if let snapshot = querySnapshot {
// print(snapshot.metadata.isFromCache)
completion(.success(snapshot.documents.map { document -> Collection in
return Collection(document: document)
}))
} else if let error = error {
completion(.failure(error))
}
}
}
NOTE: You might also add the .limit(to:1)
to your lastSnapshot check, since the way it is now, it will retrieve all the documents, of course, if that is your intended logic, ignore this note.
EDIT:
If you want to use Query Cursor Pagination, you have can follow this example where the listener is located outside of the fetchCollection function and it's executed everytime a getDocuments() is triggered and mounts the new query to be executed:
//initial state of your app
var results = "nil";
//initial query
var query = db.collection("foo").document("bar").limit(to: 1);
//everytime you need more data fetched and on database updates to your snapshot this will be triggered
query.addSnapshotListener { querySnapshot, error in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: \(error!)")
return
}
snapshot.documentChanges.forEach {
// update data
}
let next = db.collection("foo").document("bar").start(afterDocument: result.last!).limit(to: 1);
query = next;
};
//initial load of data
fetchCollection();
//simply executes the query
func fetchCollection() {
query.getDocuments { (document, error) in
if let document = document, document.exists {
if(result == "nil"){
result = document.data().map();
}else{
result.map({ document.data() })
}
} else {
print("Document does not exist")
}
}
}
NOTE: Keep in mind that this is a untested example but it might be a good starting point to what you need in your app, Also, I use this documentation for the realtime updates on the listener, you might find some more information on that link.
Upvotes: 2