I have a document with fields structured something like this:
Is there any way to count the number of nested maps? So in this case, I want to show that I have two "elements",
JGBQBnFX23Mh5NL4W8f3N1E5Czu1 and JGBQBnFX23Mh5NL4W8f3N1E5Czu1
Right now, I only know how to retrieve everything under "attendees" using
db.collection("Collection")
.document(eventID)
.getDocument { doc, err in
if let err = err {
print(err.localizedDescription)
return
}
let data = doc?.data()
let attendees = data?["attendees"]
print(attendees)
}
But I do not know how to get the total count (of my document IDs)
Keep in mind I will not be able to use arrays in my scenario. Thanks!
It turns out it could be fixed by changing
let attendees = data?["attendees"]
to
let attendees = data?["attendees"] as? Dictionary<String, Any>
and then you could get the total count by doing attendees?.count
I am adding a snapshot listener to a firebase Collection/Document/Collection to get the data as it is updated
The issue I am having is I run through a for loop and as it gets the i in the for loop I then use that string (collection name), to direct the snapshot listener, when the data comes in it will add to the data already there rather than change the data. because it doesn't touch the code to get the collection name, as far as I know.
What I need to do is to be able to add the data to a dictionary that has [String:Any], so I can have the ["collection name":name, "document name": document id, "info":document data], this is fine on first run but when data is changed it only get the data from the changed listener but I don't know how I can get it to get the collection name so I can remove that document from the dictionary before adding the new data.
func getClockOutData(completion: #escaping (_ finished: Bool) -> ()) {
for i in projectsList {
Firestore.firestore().collection("Projects").document(i).collection("TimeSheets").addSnapshotListener { (snapshot, err) in
if let err = err {
print("Tony the error was \(err.localizedDescription)")
} else {
print("Tony dataC A get is \(i)")
if let projDocs = snapshot?.documents {
print("Tony dataC get is \(i)")
for d in projDocs {
let dataG = d.data()
let dateG = d.documentID
let dataCT = ["project":i, "dateG":dateG, "inf":dataG] as [String : Any]
print("Tony dataC is \(dataCT)")
self.dataC.append(dataCT)
}
completion(true)
}
}
}
}
}
How can I get the project name (i) when the snapshot fires again with the changes?
I want that so I can create a for loop to check the project and add all the data from the project to a dict with all the same project name grouped and then run through that loop when there are changes to remove the project info before it is re appended
When you're retrieving the documents from the subcollection, no data from the parent document is retrieved.
So you will either have to load the parent document to get that information, or get the information from the context where you make this request (as likely you've loaded the parent document before as part of determining projectsList).
I have this code here and I want to delete certain value inside the array "Answered". Is there a simple way to access the first value in the array? This is right but shows what I want to happen "Answered[0]" <- I want to get the first value in that array and delete it. Thank you in Advance
let uid = Auth.auth().currentUser?.uid
print(self.randomArray)
let wash = db.collection("users").document(uid!)
wash.updateData([
"Answered": FieldValue.arrayUnion([self.randomArray])
])
}
if(self.check.isEmpty != true){
self.whichQuestion = self.check[0]
self.whichQuestionString = String(self.whichQuestion)
db.collection("users").document(uid!).updateData([
"Answered": FieldValue.delete(),
]) { err in
if let err = err {
print("Error updating document: \(err)")
} else {
print("Document successfully updated")
}
}
If your array contains unique values however, you can remove the item with:
self.whichQuestionString = String(self.whichQuestion)
db.collection("users").document(uid!).updateData([
"regions": FieldValue.arrayRemove([whichQuestionString])
])
If you only know the index of the item, there is no way to remove it without knowing the entire array.
The recipe for this is:
Read the document from Firestore
Modify the array in your application code
Write the entire modified array back to Firestore
Also see (none of which unfortunately are for Swift):
Is there any way to update a specific index from the array in Firestore
Delete data from Firestore dynamically
I'm using Firebase's Firestore to store data for my iOS app. In Firestore, I have two collections. One called songs and one called playlists. The songs collection contain many documents and each document has a single song info inside of it. Here's what a songs document looks like.
Then, in my collection playlists it contains some documents which are playlists. Here's an example of a playlists document.
I want to be able to display a playlist, and then get the songTitles from that playlist from my songs collection. So, I've done this:
var testPlaylistTitles = [String]()
db.collection("playlists").whereField("title", isEqualTo: "Test Playlist").getDocuments { (snapshot, error) in
for document in (snapshot?.documents)! {
self.testPlaylistTitles = ((document.data()["songTitles"] as? [String])!)
}
}
This makes the variable testPlaylistTitles = ["Test Song", "Test Song 2", "Test Song 3"]. And that's great. But this is where it goes wrong. I want to get information about each one of these songs from my songs collection. So, I create a for loop and loop through the songs to append to my other arrays of artist, images, and titles. Just for this example, I'll use a variable of returedTitles.
var testPlaylistTitles = [String]()
db.collection("playlists").whereField("title", isEqualTo: "Test Playlist").getDocuments { (snapshot, error) in
for document in (snapshot?.documents)! {
testPlaylistTitles = ((document.data()["songTitles"] as? [String])!)
}
var returedTitles = [String]()
for i in 0...testPlaylistTitles.count-1 {
db.collection("songs").whereField("title", isEqualTo: testPlaylistTitles[i]).getDocuments(completion: { (snapshot, error) in
for document in (snapshot?.documents)! {
returedTitles.append((document.data()["title"] as? String)!)
}
})
}
}
Now, returedTitles is equal to a shuffled version of the original testPlaylistTitles. I thought at first that somehow it was ordering testPlaylistTitles by alphabetical order, but it isn't. Does anyone have any ideas on what is happening and how to fix it! Thanks a lot! This has been stumping me for a while.
I have no idea of how to do this in swift, but I would change the data structure of the songs on the playlist document to a object where the songs are the keys and the order the value, then, in the second loop in your query, you use the keys to return the values and you should be able to sort by the values.
{
description: "test",
songs: {
"Test Song 1": 1,
"Test Song 2": 3,
"Test Song 4": 2
},
title: "Test playlist"
}
What I may also suggest is to use the ID's of the songs in the list, as you are retrieving the data anyway at a later stage.
It looks like you're depending on the order of execution of the queries in the for loop. Bear in mind that getDocuments() is asynchronous, meaning it returns immediately, and the results appears in the callback some time later (there is no guarantee how long it will take). Because it's asynchronous, you're effectively kicking off testPlaylistTitles.count nubmer of queries all happening at the same time, each without regard to the other. As a result, the array you're building is going to accumulate the results in an undefined order. To help visualize this, put a log message in each callback to see what order they're actually being invoked. Include contents of the array each time as well, to see how the array is being built.
If the order of the results matters, you will need to figure out what that order is yourself. If that means performing the queries one after the other in order (which would be an overall performance hit), then you'll have to code it that way.
But the bottom line is that you'll need to think carefully about doing asynchronous work. Please read this blog for more information about why Firebase APIs are asynchronous.
As Doug points out, the issue is that the calls are asynchronous, so they won't necessarily be added to the array in the order you start them in--they are added when the data becomes available. There are a few ways to resolve this, but one suggestion to get you started would be to initialize your arrays such that their size is equal to the size of testPlaylistTitles. Then, when you get the data, you insert it in that specific location in the array instead of appending it to the end of the array.
var testPlaylistTitles = [String]()
db.collection("playlists").whereField("title", isEqualTo: "Test Playlist").getDocuments { (snapshot, error) in
for document in (snapshot?.documents)! {
testPlaylistTitles = ((document.data()["songTitles"] as? [String])!)
}
var returedTitles = [String](repeating: "title", count: testPlaylistTitles.count)
for i in 0...testPlaylistTitles.count-1 {
db.collection("songs").whereField("title", isEqualTo: testPlaylistTitles[i]).getDocuments(completion: { (snapshot, error) in
if (snapshot!.documents.count) > 0 {
let doc = (snapshot?.documents.first)!
returedTitles[i] = ((doc.data()["title"] as? String)!)
}
})
}
}
In this code, I've initialized an array the size of testPlaylistTitles. The value of each index is just "title" to begin with, but is then replaced with the value from Firestore once it is downloaded.
var objectarray = [PFObject]()
func populateTable() {
query.findObjectsInBackgroundWithBlock { (objects, error) in
self.objectarray.removeAll(keepCapacity: true)
self.searchTableView.reloadData()
if error == nil {
Above is the query I am doing and the below 2 codes are what I can use to use the query to populate a array.
if let objects = objects as [PFObject]! {
self.objectarray = Array(objects.generate())
}
Is there any difference with running this code above to populate my array or running the code below?
for object in objects! {
self.objectarray.append(object)
}
Doing either works to load onto my tableView. Also another question regarding Parse. After doing the above, the user doesn't download PFFiles from the background until I run
getDataInBackgroundWithBlock
right? I want to know if it'd be beneficial to save smaller versions of images onto the server.