I'm trying to loop through children in my Firebase Database to retrieve a nested key.
My database is structured like this:
"Users" : {
"Username" : {
"Favorites" : {
"Location" : {
"Latitude" : 123,
"LocationName" : "San Francisco",
"Longitude" : 123
},
"Location2" : {
"Latitude" : 123,
"LocationName" : "London",
"Longitude" : 123
}
}
}
}
I am trying to print out all of the "LocationName" keys, and am able to print one instance of this key, but not able to loop through and print all instances of this key.
I'm not sure where in my for loop I'm going wrong?
The code I'm working with is below.
FIRApp.configure()
let databaseRef = FIRDatabase.database().reference().child("Users").child("Username").child("Favorites")
let databaseHandle = databaseRef.observe(.value, with: { (snapshot) in
for item in snapshot.children {
if let dbLocation = snapshot.childSnapshot(forPath: "LocationName") as? String {
print (dbLocation)
}
print(item)
}
})
I'm very new to Swift, and even newer to Firebase, so any help would be greatly appreciated!!
The problem in your code is that snapshot refers to the Favorites node – instead of looking for LocationName there, you should look for it inside each of the Location child nodes. Therefore your loop should look something like this:
let databaseHandle = databaseRef.observe(.value, with: { snapshot in
for child in snapshot.children {
let childSnapshot = snapshot.childSnapshotForPath(child.key)
if let dbLocation = childSnapshot.value["LocationName"] as? String {
print(dbLocation)
}
}
})
Related
I am trying to list all the users from a firebase node in a table view (this is working) but would then like to remove the current user from the list (this is not working).
I've tried to use removeValue() but it still ran with the current user on the table view - also I don't want to remove the user from firebase
I then tried to make it run only if the the user autoId is not equal to the current users Id but it still shows up on the table view cell
Would appreciate any help :)
My firebase structure is like this:
"users" : {
"8x1SGi2P0KVOKm4i60JQLP1bdU63" : {
"fullname" : "bbbb",
"profileImageUrl" : "https://firebasestorage.googleapis.com/v0/b/pinion-4896b.appspot.com/o/profile_image%2F8x1SGi2P0KVOKm4i60JQLP1bdU63?alt=media&token=7932b14f-c2d8-46fd-9dd1-c607217fe8d3",
},
"B7rwHiCTlphZjKXfPSEzkIwl8RH2" : {
"fullname" : "Aaaa ",
"profileImageUrl" : "https://firebasestorage.googleapis.com/v0/b/pinion-4896b.appspot.com/o/profile_image%2FB7rwHiCTlphZjKXfPSEzkIwl8RH2?alt=media&token=072e1f41-935e-430d-af99-dc640381f8e6",
},
"FRuuk20CHrhNlYIBmgN4TTz3Cxn1" : {
"fullname" : "eeee",
"profileImageUrl" : "https://firebasestorage.googleapis.com/v0/b/pinion-4896b.appspot.com/o/profile_image%2FFRuuk20CHrhNlYIBmgN4TTz3Cxn1?alt=media&token=bf89b903-a51a-4d6d-bdef-fe2667d78842",
},
Code to which lists users:
func observeUsers(completion: #escaping (UserModel) -> Void) {
REF_USERS.observeSingleEvent(of: .childAdded, with: { snapshot in
if let dict = snapshot.value as? [String: Any] {
let user = UserModel.transformUser(dict: dict, key: snapshot.key)
//line below used first to remove value from listing on table view
//Api.User.REF_USERS.child("users").child(Api.User.CURRENT_USER!.uid).removeValue()
//line below - if user autoID is not equal to the current user then list
if user.id != Api.User.CURRENT_USER?.uid {
completion(user)
}
}
})
}
EDIT:
func loadUsers() {
var allUsers = [String]()
Api.User.REF_USERS.observe(.value, with: { snapshot in
for child in snapshot.children { //build the array of keys
let snap = child as! DataSnapshot
let key = snap.key
allUsers.append(key)
print(allUsers)
}
})
}
instead of removing try this
create model of your tabledata
then create an empty array of it
while fetching the data append all except that which has same currentuser.uid
then reload the tableview this will show all the data except the current user
this is the code as promised:
but this need a little modification in your database
make your database like this
"users":{
"childbyautid":{
"fullname": "name",
"profileimageurl": "your url",
"userid": "userid"
}
then you can write like this
var myArr = [String]()
Database.database().reference.child("users").observe(.value){(snapshot) in
if snapshot.childcount > 1 { self.myArr.removeAll()
for data in snapshot.children.allObjects as! [DataSnapshot]{
if let d = data.value as? [string: any]{
if Auth.auth.currentuser.uid != d["userid"]{
myArr.append(d["name"]}else{print("this is the user itself"}}}
You can control this in you loadUsers by adding userId check like below
func loadUsers() {
var allUsers = [String]()
Api.User.REF_USERS.observe(.value, with: { snapshot in
for child in snapshot.children { //build the array of keys
let snap = child as! DataSnapshot
let key = snap.key
if key != currentUser.id{
allUsers.append(key)
}
print(allUsers)
}
})
}
I'm creating an app that allows notes to be shared. I'm having trouble with the Firebase database and feel that the amount of lines of code I am using to get information the user needs is more than I actually need and on top of that doesn’t work. Below is a screenshot of my database and a show of the code I’ve used. There are 3 different account types in my app. Parent, Child, and Other.
The Child can only see their own notes
The Parent can see everyone who’s written notes in for their code
The Other is supposed to see their notes and the parents notes per
code.
I am attempting to show the users whose notes can be seen in a collection view. So far it works for Parents and Children but not for the 'Other' which I am trying to fix.
This is my database. Right now, a Parent makes the account which generates a unique code. Then an Other or Child can join by typing the code in on a ViewController. For a child account it will automatically add the code to the account of the child in the user section and for the Other it is supposed to make a new section (I added a picture of what it looked like before I changed it). All users that have a certain code are added to the code section under that code.
{
"codes" : {
"ParVWb" : {
"totalUsers" : 2,
"users" : {
"VWbSR8qQyWd9deka6tgbxFC9ahs1" : {
"numberofNotes" : 0
},
"qm12qgkWfdbtSMMx58wIrxKuLKh1" : {
"numberofNotes" : 0
}
}
}
},
"users" : {
"JN47TBdI4EYX0NvoSHN21o1xFH92" : {
"email" : "other1#restart.mocs",
"fullName" : "Other 1 Name",
"type" : "Other",
"uid" : "JN47TBdI4EYX0NvoSHN21o1xFH92"
},
"VWbSR8qQyWd9deka6tgbxFC9ahs1" : {
"code" : "ParVWb",
"email" : "parentemail#restart.mocs",
"fullName" : "Parent Name",
"type" : "Parent",
"uid" : "VWbSR8qQyWd9deka6tgbxFC9ahs1"
},
"qm12qgkWfdbtSMMx58wIrxKuLKh1" : {
"code" : "ParVWb",
"email" : "childname#restart.mocs",
"fullName" : "Child Name",
"type" : "Child",
"uid" : "qm12qgkWfdbtSMMx58wIrxKuLKh1"
}
}
}
I used to have the use section look like this but now because of the large amount of reference blocks, it doesn't run the part that adds the "codes" section to the 'Other' user.
This is the function I have in my CollectionViewController that obtains all the needed users depending on the currentUser type.
func fetchConnections() {
ref.child("users").child(userID!).observe(.value, with: { (snapshot) in
let user = User(snapshot: snapshot)
if user.type == "Parent" {
let code = user.code as? String
let codeRef = databaseRef.child("codes").child(code!).child("users")
codeRef.observe(.value, with: { (codeSnapshot) in
if let users = codeSnapshot.value as? [String:AnyObject] {
DispatchQueue.main.async(execute: {
for user in users {
if let dict = user.value as? [String: Any] {
let userInformation = cellInfo(uid: user.0, numberofNotes: (dict["numberofNotes"] as? Int)!, code: self.code)
self.userCollection.append(userInformation)
self.collectionView.reloadData()
}
}
})
}
}) { (error) in
print(error.localizedDescription)
}
} else if user.type == "Child" {
let code = user.code
self.ref.child("codes").child(code!).child("users").child(self.userID!).observe(.value, with: { (snapshot) in
if let dict = snapshot.value as? [String: Any] {
print(user.key, (dict["numberofNotes"] as? Int)!)
let userInformation = cellInfo(uid: (user.key as? String)!, numberofNotes: (dict["numberofNotes"] as? Int)!, code: "code")
self.userCollection.append(userInformation)
self.collectionView.reloadData()
}
})
} else if user.type == "Other" {
self.ref.child("users").child(self.userID!).child("codes").observe(.value, with: { (snap) in
if let codes = snap.value as? [String:AnyObject] {
for code in codes {
if code.key == "numberofCodes" {
print("ignore")
} else { // If it's an actual code
self.ref.child("users").child(code.0).observeSingleEvent(of: .value, with: { (codeSnap) in
let user = User(snapshot: codeSnap)
let codetoFetch = user.code
self.ref.child("codes").child(codetoFetch!).child("users").child(self.userID!).observe(.value, with: {(codeSnapshot) in
if let users = codeSnapshot.value as? [String:AnyObject] {
DispatchQueue.main.async(execute: {
for user in users {
if let dict = user.value as? [String: Any] {
let userInformation = cellInfo(uid: code.0, numberofNotes: (dict["numberofNotes"] as? Int)!, code: self.code)
//self.userCollection.append(userInformation)
//self.collectionView.reloadData()
}
}
})
}
})
})
}
}
}
})
}
})
}
How can I obtain all of the codes that the 'Other' is a part of and all of the UID's & NumberofNotes for the 'Parents' of that code so that I can put them into the collectionView? (This code works for Parent and Child but if the way I'm doing it is long and ineffective please let me know.)
I'm trying to query my Firebase database to find users of a particular name or email. I've found several examples of how to do this, all of them have seemed relatively easy to follow, but none have worked as expected for me.
Here is an example of how my json data is structured.
{
"allUsers" : {
"uid0001" : {
"userInfo" : {
"email" : "firstName1.lastName1#email.com",
"firstName" : "firstName1",
"lastName" : "lastName1",
"uid" : "uid0001"
}
},
"uid0002" : {
"userInfo" : {
"email" : "firstName2.lastName2#email.com",
"firstName" : "firstName2",
"lastName" : "lastName2",
"uid" : "uid0002"
}
}
}
}
And here is a sample function of how I'm trying to query the database
func performQuery(forName queryText:String)
{
let key = "firstName"
let ref1 = firebaseDatabaseManager.allUsersRef.queryOrdered(byChild: queryText)
let ref2 = firebaseDatabaseManager.allUsersRef.queryEqual(toValue: queryText, childKey: key)
//ref.observeSingleEvent(of: .childAdded, with: {(snapshot) in
ref1.observe(.childAdded, with: {(snapshot) in
let userId = snapshot.key
if let dictionary = snapshot.value as? [String: AnyObject]
{
if let userInfo = dictionary["userInfo"] as? [String:AnyObject]
{
if
let email = userInfo["email"] as? String,
let firstName = userInfo["firstName"] as? String,
let lastName = userInfo["lastName"] as? String
{
let user = User.init(withFirst: firstName, last: lastName, userEmail: email, uid: userId)
}
}
}
})
}
You can see here I have two examples of how I'm structuring ref and two examples of how I'm observing the reference, although I've tried every possible combination that I can think of.
If I'm using ref.observe(....
The block will execute for all users at the node regardless of if queryText is actually present or not.
If I'm using ref.observeSingleEvent(of:....
The block will execute for the topmost user in the json structure.
On top of that, I've tried several variations of reference that return nothing at all.
Any help at all is appreciated!
Thanks
You need to combine queryOrderedByChild: and queryEqualToValue: to get the correct results:
let query = firebaseDatabaseManager.allUsersRef
.queryOrdered(byChild: "userInfo/" + key)
.queryEqual(toValue: queryText)
query.observe(.childAdded, ...
Try replacing
let ref2 = firebaseDatabaseManager.allUsersRef.queryEqual(toValue: queryText, childKey: key)
with
let ref2 = ref1.queryEqual(toValue: queryText)
and then call:
ref2.observe(.childAdded, with: {(snapshot) in
Since right now you are not looking for a certain user but for all users
This issue is the Firebase structure is (unnecessarily) too deep.
What you have is
"allUsers" : {
"uid0001" : {
"userInfo" : { <- This is the issue
"email" : "firstName1.lastName1#email.com",
"firstName" : "firstName1",
"lastName" : "lastName1",
"uid" : "uid0001"
}
},
It should (could) be
"allUsers" : {
"uid0001" : {
"email" : "firstName1.lastName1#email.com",
"firstName" : "firstName1",
"lastName" : "lastName1"
}
}
There's probably no need to have the userInfo node inside the uid0001 node.
Also, you probably don't need the uid stored in the node as well as using it as the key - when the node is returned you can always get the uid from the snapshot.key for each user.
That being said, you can actually do this with a deep query, but it doesn't appear to be needed in this case. (See Frank's answer as it is the correct solution for the structure posted in the question)
and to query for a specific first name using the structure I suggested
let fName = "firstName1"
let queryAllUsersRef = allUsersRef.queryOrdered(byChild: "firstName")
.queryEqual(toValue: fName)
//get all of the users with firstName1
queryRef.observeSingleEvent(of: .value, with: { snapshot in
//snapshot may return more than one user with that first name
// so iterate over the results
for snap in snapshot.children {
let userSnap = snap as! FIRDataSnapshot //each user is it's own snapshot
let userKey = commentSnap.key //the uid key of each user
let userDict = userSnap.value as! [String:AnyObject]
let email = userDict["email"] as! String
print("uid: \(userKey) has email: \(email)"
}
})
I need to arrange my firebase data by date (unix). I thought queryOrdered(byChild: "date") would do the trick. Done a search and found this which makes sense:
But when you request the .value of the snapshot, the keys+data are converted to a Dictionary. Since a dictionary does not have an extra place to put the information about the order, that information is lost when converting to a dictionary.
Using the same json but modified with unix dates...:
{
"users" : {
"alovelace" : {
"name" : "Last Year",
"date" : 1480550400
},
"eclarke" : {
"name" : "New Year Now",
"date" : 1483228800
},
"ghopper" : {
"name" : "New Year",
"date" : 1483228800
}
}
}
... how to sort when my code is like this:
DataService.ds.REF_INCOMES.queryOrdered(byChild: "date").observe(.value, with: { (snapshot) in
if let snapshot = snapshot.children.allObjects as? [FIRDataSnapshot] {
print(snapshot)
for snap in snapshot {
if let incomeDict = snap.value as? [String: AnyObject] { // What needs to change here?
let key = snap.key
let income = Income(incomeId: key, incomeData: incomeDict)
self.incomes.append(income)
self.incomes.reverse()
}
}
}
self.tableView.reloadData()
})
The image below, "Last Year" should be last but it's not:
I have a ruby mentality so Im lost with swift. Thanks.
I think what you're missing here is the sort function, Please try the code below and let me know what are the results:
DataService.ds.REF_INCOMES.queryOrdered(byChild: "date").observe(.value, with: { (snapshot) in
guard let usersSnapshot = snapshot.value as? [String:NSObject] else{
return
}
let users = usersSnapshot["users"] as! [String:AnyObject]
let sorted = users.sorted{($0.0.value["date"] as! Double) > ($0.1.value["date"] as! Double)}
print(sorted) // It should be: New Year Now, New Year, Last Year
})
I believe the problem is that you are observing by .value, which essentially ignores the order by. Try to observe by .childAdded, which does respect the order by operation.
For more info, read the first "Pro Tip": https://howtofirebase.com/collection-queries-with-firebase-b95a0193745d
I need to make multiple observations, but I don't know how.
Here is my database structure:
"Posts" : {
"f934f8j3f8" : {
"data" : "",
"date" : "",
"userid" : ""
}
},
"Users" : {
"BusWttqaf9bWP224EQ6lOEJezLO2" : {
"Country" : "",
"DOB" : "",
"Posts" : {
"f934f8j3f8" : true
},
"Profilepic" : "",
"name" : "",
"phonenumber" : ""
}
I want to observe the posts and I write the code and it works great, but I also want to get the name of the user who posted this post but when I wrote save the name and use it it gives me null. Here is my code.
DataServices.ds.REF_POSTS.queryOrderedByKey().observe(.value,
with: { (snapshot) in
self.posts = []
if let snapshot = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshot {
if let postsDict = snap.value as? Dictionary<String, AnyObject> {
let key = snap.key
let userID = "BusWttqaf9bWP224EQ6lOEJezLO2"
DataServices.ds.REF_USERS.child(userID).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let postusername = value?["name"] as? String ?? ""
})
print(" ------ User name : \(postusername) ------")
})
print(" ------ User name 2 : \(postusername) ------")
let post = Posts(postKey: key, postData: postsDict)
self.posts.append(post)
The first print statement prints the username, but the second one prints nothing.
Thanks in advance.
Firebase is asynchronous so you can't operate on a variable until Firebase populates it within it's closure. Additionally code is faster than the internet so any statements following a closure will occur before the statements within the closure.
The flow would be as follows
Query for the post {
get the user id from the post inside this closure
query for the user info {
create the post inside this second closure
append the data to the array inside this second closure
reload tableview etc inside this second closure
}
}
Something like this edited code
self.posts = []
myPostsRef.queryOrderedByKey().observe(.value, with: { (snapshot) in
if let snapshot = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshot {
if let postsDict = snap.value as? Dictionary<String, AnyObject> {
let key = snap.key
let userID = "BusWttqaf9bWP224EQ6lOEJezLO2"
myUsersRef.child(userID).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let userName = value?["name"] as? String ?? ""
let post = Posts(postKey: key, postData: postsDict, name:userName)
self.posts.append(post)
})
}
}
}
})
You're not using the postusername inside the closure so I added that to the Posts initialization.
Also, the self.posts = [] is going to reset the posts array any time there's a change in the posts node - you may want to consider loading the array first, and then watch for adds, changes, or deletes and just update the posts array with single changes instead of reloading the entire array each time.
Edit:
A comment was made about the data not being available outside the loop. Here is a very simplified and tested version. Clicking button one populates the array from Firebase with a series of strings, clicking button 2 prints the array.
var posts = [String]()
func doButton1Action() {
let postsRef = ref.child("posts")
self.posts = []
postsRef.observe(.value, with: { (snapshot) in
if let snapshot = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshot {
let value = snap.value as! String
self.posts.append(value)
}
}
})
}
func doButton2Action() {
print(posts)
}