Hello there I have nested database with collection(quotes)>document(uid)>collection(quote)>document(id)
When I try to fetch the quote, I can only fetch for current user. How can I loop through uid and get everything inside quote collection for every user.
My code for fetching the quotes:
func fetchQuote() {
guard let uid = Auth.auth().currentUser?.uid else {
return
}
Firestore.firestore().collection("quotes")
.document(uid).collection("quote")
.addSnapshotListener { querySnapshot, error in
if let error = error {
print("There was an error while fetch the quotes.")
return
}
querySnapshot?.documentChanges.forEach({ change in
if change.type == .added{
let data = change.document.data()
self.quotes.append(.init(documentId:change.document.documentID, data: data))
}
})
}
}
I tried to remove the following:
.document(uid).collection("quote")
What I did is use of .collectionGroup()
Firestore.firestore().collectionGroup("quote").getDocuments(){ querySnapshot, error in
if let error = error {
print("There was an error \(error)")
return
}
querySnapshot?.documentChanges.forEach({ change in
if change.type == .added{
let data = change.document.data()
self.quotes.append(.init(documentId:change.document.documentID, data: data))
}
})
}
Related
I try to find a solution for paginate a firebase query on ios/swift but I couldn't build algorithm for my state.
My method is like this:
func downloadData(completion: #escaping ([Post]) -> Void) {
// download data with pagination
let firestoreDatabase = Firestore.firestore()
var first = firestoreDatabase.collection("posts").order(by: "date", descending: true).limit(to: 5)
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
}
self.postList.removeAll(keepingCapacity: false)
DispatchQueue.global().async {
for document in snapshot.documents {
// getting data from document stuff ...
self.postList.append(self.post)
}
completion(self.postList)
}
// how can I repeat this query as long as lastSnapshot exist
firestoreDatabase.collection("posts").order(by: "date", descending: true).start(afterDocument: lastSnapshot).addSnapshotListener { querySnapshot, error in
}
}
}
I tried following mindset but it didn't work, and entered an infinite loop. I didn't understand why it is.
func downloadData(completion: #escaping ([Post]) -> Void) {
// download data with pagination
let firestoreDatabase = Firestore.firestore()
var first = firestoreDatabase.collection("posts").order(by: "date", descending: true).limit(to: 5)
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
}
self.postList.removeAll(keepingCapacity: false)
DispatchQueue.global().async {
for document in snapshot.documents {
// geting data from document stuff ...
self.postList.append(self.post)
}
completion(self.postList)
}
repeat {
firestoreDatabase.collection("posts").order(by: "date", descending: true).start(afterDocument: lastSnapshot).addSnapshotListener { querySnapshot, 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
}
self.postList.removeAll(keepingCapacity: false)
DispatchQueue.global().async {
for document in snapshot.documents {
// getting data from document stuff ...
self.postList.append(self.post)
}
completion(self.postList)
}
lastSnapshot = snapshot.documents.last
}
} while(lastSnapshot.exists)
}
}
I think lastSnapshot must be nil after the query loop but it is appear that it is still exist.
how can I fix lastSnapshot problem? Or is there different mindset / easiest way to paginate?
In firebase documents, it says just use this but how can we repeat query that has " .start(afterDocument: lastSnapshot) " stuff?
First and foremost, for plain-vanilla pagination, don't use a snapshot listener when fetching documents. You can paginate documents with a snapshot listener but the process is more complex.
I've embedded my notes into the comments in the code below for clarity.
let pageSize = 5
var cursor: DocumentSnapshot?
func getFirstPage(completion: #escaping (_ posts: [Post]?) -> Void) {
let db = Firestore.firestore()
let firstPage = db.collection("posts").order(by: "date", descending: true).limit(to: pageSize)
firstPage.getDocuments { snapshot, error in
guard let snapshot = snapshot else {
// Don't leave the caller hanging on errors; return nil,
// return a Result, throw an error, do something.
completion(nil)
if let error = error {
print(error)
}
return
}
guard !snapshot.isEmpty else {
// There are no results and so there can be no more
// results to paginate; nil the cursor.
cursor = nil
// And don't leave the caller hanging, even on no
// results; return an empty array.
completion([])
return
}
// Before parsing the snapshot, manage the cursor.
if snapshot.count < pageSize {
// This snapshot is smaller than a page size and so
// there can be no more results to paginate; nil
// the cursor.
cursor = nil
} else {
// This snapshot is a full page size and so there
// could potentially be more results to paginate;
// set the cursor.
cursor = snapshot.documents.last
}
var posts: [Post] = []
for doc in snapshot.documents {
posts.append(newPost) // pseudo code
}
completion(posts)
}
}
func continuePages(completion: #escaping (_ posts: [Post]?) -> Void) {
guard let cursor = cursor else {
return
}
let db = Firestore.firestore()
let nextPage = db.collection("posts").order(by: "date", descending: true).limit(to: pageSize).start(afterDocument: cursor)
nextPage.getDocuments { snapshot, error in
guard let snapshot = snapshot else {
completion(nil)
if let error = error {
print(error)
}
return
}
guard !snapshot.isEmpty else {
// There are no results and so there can be no more
// results to paginate; nil the cursor.
cursor = nil
completion([])
return
}
// Before parsing the snapshot, manage the cursor.
if snapshot.count < pageSize {
// This snapshot is smaller than a page size and so
// there can be no more results to paginate; nil
// the cursor.
cursor = nil
} else {
// This snapshot is a full page size and so there
// could potentially be more results to paginate;
// set the cursor.
cursor = snapshot.documents.last
}
var morePosts: [Post] = []
for doc in snapshot.documents {
morePosts.append(newPost) // pseudo code
}
completion(morePosts)
}
}
I'm trying with no success on finding a way to retrieve only a single document instead of an array of documents from Firestore below is the code that I'm using for fetching ad array. Someone has suggestion on how to change fro getting only a document?
#Published var plantData: [PlantDataModel] = [] -> here I don't want an array
func loadData() {
print("FIREBASE LOADING DETAIL DATA VIEW")
db.collection("plantsData").whereField("plantId", isEqualTo: plant.idPlant).addSnapshotListener { querySnapshot, error in
if let querySnapshot = querySnapshot {
self.plantData = querySnapshot.documents.compactMap { document in
do {
let x = try document.data(as: PlantDataModel.self)
return x
} catch let error {
print("Errore fetching data: \(error)")
}
return nil
}
}
}
}
thank you
Replace
self.plantData = querySnapshot.documents.compactMap { document in
do {
let x = try document.data(as: PlantDataModel.self)
return x
} catch let error {
print("Errore fetching data: \(error)")
}
return nil
}
With
if let first = querySnapshot.documents.first {
do {
let x = try first.data(as: PlantDataModel.self)
self.plantData.append(x)
} catch let error {
print("Errore fetching data: \(error)")
}
}
I have been working on Firestore for retrieving data, when I tried to get data from collection->document id-> field. refer the below screen shot, I need to check companyCode matches with user entered companyCode.text
I tried with below code, need to check whether the user entered companyCodeLabel.text matches document "companyCode" and also get documentId. Can anyone suggest how to solve this?
guard let code = companyCodeLabel.text else { return }
let docRef = db.collection("Company").whereField("companyCode", isEqualTo: code).limit(to: 1)
docRef.getDocuments { (querysnapshot, error) in
if error != nil {
print("Document Error: ", error!)
} else {
if let doc = querysnapshot?.documents, !doc.isEmpty {
print("Document is present.")
}
}
}
Even tried to print the field value in collection but still have crash and same error nil
self.db.collection("Company").getDocuments { (snapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in snapshot!.documents {
let docId = document.documentID
let compCode = document.get("companyCode") as! String
let compName = document.get("companyName") as! String
print(docId, compCode, compName)
}
}
}
I tried to call in wrong db, I was trying var db = Firestore!,
The correct solutions is
Firestore.firestore().collection("Company").getDocuments { (snapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in snapshot!.documents {
let docId = document.documentID
let compCode = document.get("companyCode") as! String
let compName = document.get("companyName") as! String
print(docId, compCode, compName)
}
}
I have implemented pagination firestore query to return results 5 at a time.
It first starts by creating a listener and pulling 5 results. On the pull to refresh it pulls 5 more results. On the 2nd pull to refresh it for some reason pulls 10 results. On the 3rd pull to refresh it doesn't pull any results. After looking at the data some data is missing.
Below is my MessageListener function:
func createMessageListener() {
reference = db.collection(["chats", channel.id!, "messages"].joined(separator: "/"))
let first = reference?.order(by: "created", descending: true).limit(to: 5)
messageListener = first!.addSnapshotListener { querySnapshot, error in
guard let snapshot = querySnapshot else {
print("Error listening for channel updates: \(error?.localizedDescription ?? "No error")")
return
}
snapshot.documentChanges.forEach { change in
self.handleDocumentChange(change)
self.updateReadStatus()
}
guard let lastSnapshot = snapshot.documents.last else {
// The collection is empty.
self.hasMoreMessages = false
return
}
self.last = self.reference?.order(by: "created", descending: true).start(afterDocument: lastSnapshot).limit(to: 5)
}
}
Below is my loadMoreMessages function:
func loadMoreMessages() {
if hasMoreMessages {
last!.addSnapshotListener { querySnapshot, error in
guard let snapshot = querySnapshot else {
print("Error listening for channel updates: \(error?.localizedDescription ?? "No error")")
self.hasMoreMessages = false
self.delegate.viewModelDidLoadMoreMessages(hasMoreMessages: self.hasMoreMessages)
return
}
self.moreMessagesLoaded = true
snapshot.documentChanges.forEach { change in
self.handleDocumentChange(change)
}
self.delegate.viewModelDidLoadMoreMessages(hasMoreMessages: true)
guard let lastSnapshot = snapshot.documents.last else {
// The collection is empty.
self.hasMoreMessages = false
return
}
self.last = self.reference?.start(afterDocument: lastSnapshot)
}
} else {
self.delegate.viewModelDidLoadMoreMessages(hasMoreMessages: self.hasMoreMessages)
}
}
Not too sure what is going on. If in the create listener function i change the first limit to 35 for example. It will pull all the data.
Thanks in advance for any help
I'm Calling this function to retrieve users from firestore:
Each time a user is modify I want to update the users array.
func fetchUsers( complete: #escaping ( _ success: Bool, _ users: [User], _ error: Error? )->()) {
//self.users = []
let circleId = UserDefaults.standard.string(forKey: "circleId") ?? ""
DataService.call.REF_CIRCLES.document(circleId).collection("insiders").order(by: "position", descending: false).addSnapshotListener { querySnapshot, error in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: \(error!)")
return
}
snapshot.documentChanges.forEach { diff in
if (diff.type == .added) {
let data = diff.document.data()
let id = diff.document.documentID
let user = User(key: id, data: data)
self.users.append(user)
complete(true, self.users, nil)
}
if (diff.type == .modified) {
// Update users array if user data is modified
if !self.users.isEmpty {
self.users = []
let data = diff.document.data()
let id = diff.document.documentID
let user = User(key: id, data: data)
self.users.append(user)
complete(true, self.users, nil)
}
}
if (diff.type == .removed) {
print("Removed user: \(diff.document.data())")
}
}
}
}
However my array always return 1 if there's only one user modified and my collectionview reload and then only show one user! How do I
return all the users even if only one was modified?
Thanks
New Function:
func fetchUsers( complete: #escaping ( _ success: Bool, _ users: [User], _ error: Error? )->()) {
self.users = []
let circleId = UserDefaults.standard.string(forKey: "circleId") ?? ""
DataService.call.REF_CIRCLES.document(circleId).collection("insiders").order(by: "position", descending: false).addSnapshotListener { querySnapshot, error in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: \(error!)")
return
}
snapshot.documents.forEach { diff in
let data = diff.data()
let id = diff.documentID
let user = User(key: id, data: data)
self.users.append(user)
complete(true, self.users, nil)
}
}
}
Now I'm seeing a lot of duplicate users in my collectionview
You're looping over QuerySnapshot.documentChanges, which only contains documents that changed since the last snapshot.
To get all documents in the query (instead of just the modified ones), loop over QuerySnapshot.documents instead:
DataService.call.REF_CIRCLES.document(circleId).collection("insiders").order(by: "position", descending: false).addSnapshotListener { querySnapshot, error in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: \(error!)")
return
}
snapshot.documents.forEach { diff in
...