Swift Query Into Firebase Firestore Nested Array - ios

I have nested data that I'd like to display in a tableView.
My data is structured like so...
/users
/userid
name: "John"
age: 23
/likedPosts
0:post1
1:post2
For the tableview I'd like to display these posts (which have their own collection of data).
In order to do that I need to...
1) Get the count of the array and
2) Query the users likedPost array values to get the content of the post.
I'm currently using the getDocument function and can't figure it out.
for example...
func getUserLikedPosts() {
if let user = Auth.auth().currentUser {
let userFS = Firestore.firestore().collection("users").document(user.uid)
userFS.getDocument(completion: { (snapshot, error) in
print(snapshot?)
})
}
}
This doesn't even print out the nested array?

Something like this should work:
func getUserLikedPosts() {
if let user = Auth.auth().currentUser {
let userFS = Firestore.firestore().collection("users").document(user.uid).collection(“likedPosts”)
userFS.getDocuments(completion: { (snapshot, error) in
print(snapshot?)
})
}
}
With Firestore you need to drill down to the actual node you want, unlike Firebase where you can access child snapshots.
So after a quick discussion, with the OP, it was that the user in question didn’t have a likedPost object.
To access the array (Please be aware I haven’t tested this code, it is an example):
If let doc = document, let array = doc[“likedPost] as NSArray {
print(array)
}

Related

How do I retrieve data from Firebase and return a boolean value accurately? [duplicate]

This question already has answers here:
Storing asynchronous Cloud Firestore query results in Swift
(1 answer)
How do I save data from cloud firestore to a variable in swift?
(1 answer)
How can I change the order of functions triggered?
(1 answer)
Closed 1 year ago.
I am having trouble figuring out how to modify my code to take into account the fact that Firebase runs asynchronously.
func checkIfFriends(_ SearchUserUID: String) -> Bool {
var friendArray: [String]?
currentUserDetails.getDocument { (document, error) in
if let document = document, document.exists {
friendArray = document.data()!["Friends"] as? [String]
}
}
if let friends = friendArray {
return friends.contains(SearchUserUID)
}
return false
}
currentUserDetails is the document of the current user that stores an array of friends that the current user has under the String, "Friends", where each element of the array is the UID of the friend. I would like to check if two users are friends by first retrieving the array of friends of the current user, and then checking if that array contains the UID of the friend we are searching for.
The issue is that
currentUserDetails.getDocument { (document, error) in
if let document = document, document.exists {
friendArray = document.data()!["Friends"] as? [String]
}
}
runs asynchronously so my friendArray is considered to be nil and my method always returns false. I am very confused as to how I can modify such methods to return values and take into account the asynchronous nature of Firebase data retrieval. Could someone help me out? Thank you very much!

How can I use the section parameter to iterate over each document that's inside of a collection to access the count of a subcollection?

So I'm working on my first workout tracking app and this is my first time using Firebase/Firestore, so I'm just trying to figure out if there is a simple query that I can use for this...
Here is what my Firestore Database structure looks like:
/Users/mi9P3TrLwkQ3oDIut/Days/WZ3Q6LDuu1kja/Workouts/BpLGFREoJNzNQW/Exercises/5vRWuHlcJHc/WeightReps/cKrB0Dpf0myEDQV0
Basically I need to return a value for numberOfRowsInSection, but the value that I need to access is the number of workouts that are associated with each day of the week, and I'm not too sure how to go about using the section parameter to iterate over each day document in my Days collection in order to access the Workouts subcollections and get the count of the documents there for each day collection. Does that make sense?
I hope that the question makes sense. Any help would be greatly appreciated!
Not entirely sure if I am getting your question right but if you want to retrieve several documents with all their attributes this is how you can do it:
var counter = 0
func getData() {
let db = Firestore.firestore()
let userID = Auth.auth().currentUser!.uid
for data in self.dataSourceArray {
db.collection("users").document(userID).collection("yourCollectionName").document(data.name).collection("yourCollectionName").getDocuments() { ( querySnapshot, error) in
if let error = error {
print(error.localizedDescription)
} else {
for document in querySnapshot!.documents {
self.counter += 1
}
}
}
}
}
Is this what youre looking for ?

get a KEY, given a value, from Firebase in Swift

Have a registry of users, and we would like to input an email address, then return the UID.
Is there a way to do this in swift?
We would like the function to return this.
like we'd input user#user.com into the function and it will return gSVeU6....
Any tips and suggestions are really appreciated man.
Here is what the firebase JSON tree looks like
func findFriendsUIDFromFirebase(selectedFriendsEmail: String) {
print("Hey from findFriendsUIDFromFirebase called")
fir.child("registeredUsersOnPlatform").observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() {
guard let allTheUsers = snapshot.value as? [String:String] else {
print("Something is wrong with this snapshot")
return
}
if let selectedUserUIDFromFirebase = allTheUsers.someKey(forValue: selectedFriendsEmail) {
//DO STUFF
basically in this way we download everything locally then loop through the dictionary, but am looking for a better way, one that doesn't involving downloading the whole thing. Maybe something with a .equals()?
At the same time, for some reason when printing the dictionary, it seems to be stuck at 100 key-value pairs. when there are like 300ish pairs on the actual table. It's some clipping somewhere.
You would need to perform a Firebase query for the specific value you are looking for:
let queryEmail = "Userone#user.com"
fir.child("registeredUsersOnPlatform").queryOrderedByValue().queryEqual(toValue: queryEmail).observeSingleEvent(of: .value) { (querySnapshot) in
for result in querySnapshot.children {
let resultSnapshot = result as! DataSnapshot
print (resultSnapshot.key)
}
}
You can also limit the amount of query results you would like with .queryLimited(toFirst: x)
If you want to get Key when create User in firebase you can get them in user callBack data like:
Auth.auth().createUser(withEmail: email, password: pwd) { (user, _) in
print(user?.uid)
}
Else if you want to get key from value in normal lists data so that use queryEqualToValue then print snapshot's child key

How would I be able to append an item to a specific array on a Parse Server?

I am trying to create a comments section for my app that allows user to comment on a post. I'm currently using Parse, and I had attempted to create a row full of arrays. Each row would contain an array, that held the comments for a particular post. I am currently confused as to how I would be able to access then update the array at the given row. I've been reading the Parse documentation and guides, but haven't been able to figure this out yet. My code is below and the screenshot for my Parse page/layout is below as well. I'd appreciate it if anybody could help me out or even just recommend a better way for me to deal with user comments for a particular post.
Please note that the 'Answers' are the 'Commments'.
In the case below, Test and Dog represent the comments on one post and Cat and Meow represent the comments on another post.
NOTE: Below, comments is an array of arrays.
var post = PFObject(className: "Posts")
var query = PFQuery(className: "Posts")
query.findObjectsInBackground(block: { (objects, error) in
if let posts = objects {
for object in posts{
if let post = object as? PFObject {
if let Answers = post["Answers"] {
self.comments.append(Answers as! [String])
print(post["Answers"])
}
}
}
self.comments[self.indexPathNum].append(self.answerTextView.text)
post["Answers"] = self.comments
post.saveInBackground(block: { (success, error) in
if error != nil {
print("error")
}else {
print("Success")
}
})
}
})

Pulling Data from Firebase into Array

I was recently told to structure my Firebase differently. Before I was putting everything related to a particular user under his or her tree. I was told however to flatten it and create his or her nodes separately and then to just link that node into that users tree when you need to.
So my tree looks like this
root
card
*card autoID*
nickname: "foo"
type: "bar"
user
*user uid*
card
*card autoID*: true
I am going to add more to the card as the user progresses through the app, and if I understand how I am supposed to structure the data I will be adding it to the the card node since that card is linked to the user.
My question is how do I pull data from Firebase then into say an array or a dictionary? If it was all in one tree I would do something like this
let ref = FIRDatabase.database().reference()
let user = FIRAuth.auth()?.currentUser
let userCard = ref.child((user?.uid)!).child("card")
But since that card under the user is only a reference how do I then go to the real place where the card is...the part that has the nickname and type?
Edit
So with some help from other SO posts, the documentation, and a friend I have the code 90% working.
What I am able to do is
1) find all of the card autoID under the user node that is associated to the user and store those strings into an array # 1
2) I am able to query all of the card autoID under the node card and then find the ones that match what is in array # 1 and store them in array # 2 (the rest are ignored)
3) **Here is where I am stuck. If I am inside of the .observe then I can do what I want with the array like printing its contents. HOWEVER, if I call print outside of the .observe I get nothing...
here is my code
func pullCurrentUserCardInfo() {
let userCardsRef = ref.child("users").child((user?.uid)!).child("cards")
userCardsRef.observeSingleEvent(of: .value, with: {(snapshot) in
if let snapDict = snapshot.value as? [String: AnyObject] {
for each in snapDict {
self.usersCardRefArray.append(each.key)
self.count = Int(snapshot.childrenCount)
}
}
})
self.ref.child("cards").observe(.value, with: { (snapshot) in
if snapshot.hasChildren() {
for item in snapshot.value as! [String: AnyObject] {
for test in self.usersCardRefArray {
if test == item.key {
self.allCurrentUsersCards.append(item.key)
}
}
}
} else {
print("no children")
}
})
}
if I were to say the following inside of the function but outside of the .observe ....}) then it doesn't do anything.....
for item in allCurrentUsersCards {
print(item)
}
Am I missing something small somewhere or is this something to do with firebase?
I think there's an unneeded level of complexity here. You do not need to store (in this use case at least) a separate card for each user. There's a 1-1 relationship between user and card so just storing the card data for each user within the user node would be the best answer.
However, to answer the question directly, here's how to do it. We going to slightly alter the Firebase structure:
root
cards
*user uid* <- CHANGE
nickname: "foo"
type: "bar"
users
user uid: true <- CHANGE
Since user uid's are always unique and created for you, leverage them when working with users. So in this case just store the user uid's in the user node and that same uid in the cards node.
Create a User Class and an array to store them in. This would typically be done right inside a viewController for example
class ViewController: UIViewController {
class UserClass {
var uid = ""
var nickname = ""
var type = ""
}
var usersArray = [UserClass]()
Then, craft a Firebase observer to populate the usersArray, getting each card for each user
//iterate over all of the users, get the user and its card data
let usersRef = ref.child("users")
usersRef.observeSingleEvent(of: .value, with: { snapshot in
for snap in snapshot.children { //iterate over all users
let userSnap = snapshot as! FIRDataSnapshot
let userKey = userSnap.key //the uid of each user
//now that we have the uid, get it's card data
let thisUserCardRef = cardsRef.child("uid")
thisUserCardRef.observeSingleEvent(of: .value, with: { userSnap in
let userCardSnap = userSnap as! FIRDataSnapshot
let userCardDict = userCardSnap.value as! [String:AnyObject]
let nickname = userCardDict["nickname"]
let type = userCardDict["type"]
let aUser = UserClass()
aUser.userKey = userKey
aUser.nickname = nickname
aUser.type = type
self.usersArray.append(aUser)
//In general, this is where the tableView is refreshed
// because the user data and card data is valid at this point
//usersTableView.reload data /
})
}
})
The key here is to remember that Firebase is asynchronous and that code is way faster than the internet. So this high level example will fail most of the time
func getData() {
loadDataFromFirebase()
print("data loaded, do something with it") //<- executes before the prior line completes
}
func loadDataFromFirebase() {
someRef.observeEvent...
print("data is now valid inside observe closure")
}
This will usually result in
data loaded, do something with it
data is now valid inside observe closure
Which is opposite of what is wanted. Code executes faster than the internet so the asynchronous observe closure will occur after the data loaded... is printed. Only reference and work with firebase data inside a closure and use the closure to pace your app.
If you notice in the first example code provided - we only work with the data once it's been returned from Firebase.
Also note that we completely eliminated queries! Queries are 'heavy' by comparison to observe events and since we are leveraging the uid of each user, it's path will be known, hence the change from a node created with childByAutoId to using the uid.

Resources