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

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

Related

Cannot retrieve comments from database (FIREBASE/IOS)

I'm kind of bashing my head at the moment because I can't seem to figure out the reason why my code isn't properly loading comments from the database, It's receiving the ID's for each comment for a post from the database (post_comments) but isn't able to retrieve any data?
func loadComments() {
let postCommentRef = Database.database().reference().child("post_comments").child("6AECB02A-CC97-4ECB-8A09-702E254D4CCD")
postCommentRef.observe(.childAdded, with: {
snapshot in
print("snapshot key")
print(snapshot.key)
Database.database().reference().child("comments").child(snapshot.key).observeSingleEvent(of: .value, with: {
snapshotComment in
//print(snapshotComment.value!)
if let dict = snapshotComment.value as? [String : Any] {
let newComment = Comment().transformComment(dict: dict)
self.fetchUser(uid: newComment.uid!, completed: {
self.comments.append(newComment)
self.tableView.reloadData()
print(newComment) <- trying to retrive data, I've posted below what the output of this is.
})
//let photoUrlString = dict["photoUrl"] as! String
}
})
})
}
in my code you can see that I've placed a little print function to see what data the code spits out, here's the output from the debug log.
snapshot key
L_sWOp0w1V8DaGSK7iK
snapshot key
L_sWQI70PogYAtwjla4
snapshot key
hello <-- this is a test uid I created in the DB, treat it like any other key listed above.
as you can see the outcome of the loadComments() function doesn't achieve much.
I'm not sure if it's needed but just in case it helps I've taken a screenshot of the database to show how it actually looks below.
if any further information is required please ask and I'll provide it, I've only provided what I believe is necessary, and I'm pretty certain it's due to the way I'm retrieving data from the database.
EDIT
After some playing around I've managed to get some kind of output from the second database call, which retrieves the key in the comments database, which is the same two that are in the post comments database however, the value returns null.
func loadComments() {
let postCommentRef = Database.database().reference().child("post_comments").child("6AECB02A-CC97-4ECB-8A09-702E254D4CCD")
postCommentRef.observe(.childAdded, with: {
snapshot in
print(snapshot.key)
Database.database().reference().child("comments").child(snapshot.key).observeSingleEvent(of: .value, with: { (snapshotComment) in
print("Snapshot value")
print(snapshotComment.value)
print("Snapshot.key")
print(snapshotComment.key)
//print(snapshotComment.value!)
//if let dict = snapshotComment.value as? [String : Any] {
// let newComment = Comment().transformComment(dict: dict)
// self.fetchUser(uid: newComment.uid!, completed: {
// self.comments.append(newComment)
// self.tableView.reloadData()
// print(newComment)
// })
//let photoUrlString = dict["photoUrl"] as! String
})
})
}
The outcome of this code is as follows..
L_sWOp0w1V8DaGSK7iK <-- these two come from the snapshot.key for post_comments
L_sWQI70PogYAtwjla4 <---^
Snapshot value
Optional(<null>)
Snapshot.key
L_sWOp0w1V8DaGSK7iK
Snapshot value
Optional(<null>)
Snapshot.key
L_sWQI70PogYAtwjla4
I'm going to keep my hopes up and try figure out the source of this issue, if nobody can provide an answer to this question I'm hoping to be able to find a way to answer it myself as I believe the database structure I'm attempting to build is a lot more efficient and provides a better user experience, if I'm wrong I'd appreciate knowing a better way :)
EDIT #2
I seemed to have resolved my issue, I've posted a detailed description below of how and what was causing the issue along with the code I am using after resolving the issue
So it seems the issue was something ridiculously simple like I had thought, however, not actually having worked with firebase before it slipped right over my head, anyway after taking a look in the firebase console at the database I noticed comments would show as this https://REDACTED/comments/-L_sWQI70PogYAtwjla4
in the database, after looking closely you can see that the id for the comment begins with a hyphen, but looking in the database it's self without looking at the URL wouldn't actually reveal this, so I've managed to resolve this issue with about 4 characters as follows
Database.database().reference().child("comments").child("-" + snapshot.key).observeSingleEvent(of: .value, with: {
as you can see the 4 characters I'm speaking of is "-" = which adds the hyphen to the database query and returns the correct values; I'm not sure if this is the best way but it works so I'm happy!
here's my code after all this fuss in hopes someone facing the same issue in the future will find this question and not have to go through what I have...
func loadComments() {
let postCommentRef = Database.database().reference().child("post_comments").child("6AECB02A-CC97-4ECB-8A09-702E254D4CCD")
postCommentRef.observe(.childAdded, with: {
snapshot in
print("snapshot key")
print(snapshot.key)
Database.database().reference().child("comments").child("-" + snapshot.key).observeSingleEvent(of: .value, with: {
snapshotComment in
//print(snapshotComment.value!)
if let dict = snapshotComment.value as? [String : Any] {
let newComment = Comment().transformComment(dict: dict)
self.fetchUser(uid: newComment.uid!, completed: {
self.comments.append(newComment)
self.tableView.reloadData()
print(newComment)
})
//let photoUrlString = dict["photoUrl"] as! String
}
})
})
}

How do I search for a particular item in my database using Swift and Firebase?

So I am building an app where I add certain words to my Firebase Database. When the user enters the word they want to add, I want to check if that word already exists in the database. If it does then I would show an alert. If not, I'll add it to the database. How would I do that with Swift? Any help would be appreciated. Thank you!
Here's the database structure:
speaktext-6-----
wordList
-LWQObIw1PKWJ_B9jNfp
word: "Water"
wordType: "Noun"
You need to take into account there may be a significant number of words so loading all of the words in will not only be slow as they have to loaded and then iterated over to find the word you are looking for, but it may also overwhelm the devices memory.
A simple solution is a Firebase Query - let Firebase do the heavy lifting and just return the node you want. It will be a LOT faster and won't overwhelm the device.
Here's a function that will tell you if a word exists within your firebase structure
func findWord(aWord: String) {
let ref = self.ref.child("wordList")
let query = ref.queryOrdered(byChild: "word").queryEqual(toValue: aWord)
query.observeSingleEvent(of: .value, with: { snapshot in
if snapshot.exists() {
print("found the word")
//you could expand on this to indicate it was a Noun etc
} else {
print("\(aWord) is not in the wordList")
}
})
}
Also review the Firebase Sorting and Filtering Data Guide
*this assumes a structure of
root
wordList
word_id_0 //created with .childByAutoId
word: "Water"
You just need to make a single event request on the word child, then you can verify if it .exists() and it will return if it's there or not.
let reference = Database.database().reference().child(<#child#>).child(<#Word#>)
reference.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() {
// handle word existing in database, show alert.
} else {
// handle word not existing in database, make a request to set the word in database.
}
})
According to your comments, you wanna iterate through every element on a child, you can do it by doing this:
let reference = Database.database().reference().child("wordList")
reference.observeSingleEvent(of: .value, with: { (snapshot) in
if let words = snapshot.value as? [String : [String : Any]] {
for word in words {
if let currentWord = word.value["word"] as? String {
if currentWord == "YOUR WORD HERE" {
// handle word in database.
return
}
}
}
// handle word NOT in database.
} else {
// handle empty array.
}
})

How to get the value of a key in firebase - iOS

so this is an image of my JSON tree:
my JSON TREE
Question:
I wanted to know how can I check if the username, let's say, sean exists in the usernames. I currently have no idea on how to implement this.
What I've tried:
The key of usernames child is "theUsernameOf-userUID", and that causes the problem as userUID is dynamic and different from each user (from firebase auth), therefore I can't use:
.queryOrderedByChild("theUsernameOf-userUID").queryEqual(toValue: self.usernameTextBox.text!)
The key of usernames child can't be static like theUsername as it would only be able to have 1 value / not able to generate more node.
Thank you so much, sorry if I didn't explain clearly enough.
I would like to modify your DB structure as current one is not the correct to perform this query.
It should be like below:
Always use auto incremented keys for queries. Here usernames -> autoGeneratedKey -> yourData (Dictionary - Key-Value pair) Now you can easily check the existence of any key.
let ref = defaultDB.reference.child("usernames")
ref.queryOrdered(byChild: "username").queryEqual(toValue: "sean").observeSingleEvent(of: DataEventType.value) { (snapshot) in
if snapshot.exists() {
print("exists")
}
else {
print("doesn't exist")
}
}
Output: exists
This is the correct way to do so. Just checking for snapshot.exists() will do the job for you.
When you observe any reference in firebase, you get a DataSnapshot in return. The snapshot has a children enumerator property on which you can enumerate each child. Each of the child will be another DataSnapshot. Now, each snapshot has key and value. You want the user name? It's in the value property:
let databaseRef = Database.database().reference(withPath: "usernames")
databaseRef.observe(.value) { (snapshot) in
snapshot.children.forEach({ (child) in
if let child = child as? DataSnapshot, let value = child.value {
print(value) //"Sean", "Yuh"
// here you can check for your desired user
}
})
}
Here is how you can do it
Set the database reference to the usernames node. For example db.ref.child("usernames")
Now parse the snapshot using for loop
let usernames = snapshot.value as! NSDictionary
Now the for loop
for username in usernames{
if username.value == "Sean"{
// do whatever you want here
}
}
You could also:
Make a firebase call to usernames and make that:
let usernames = snapshot.value as? [String: AnyObject],
then all you have to do is something like
if let keyExists = usernames[YOURUSERNAMECHECKSTRING] {
//It's true
}
Just another way of looking at it.
Try This code will Help you you dont need to change your firebase structure
In Swift
Database.database().reference(withPath: "users").queryOrdered(byChild: "usernames").queryEqual(toValue: "yourUserName").observe(.value)
{ (snapshot:DataSnapshot) in
if snapshot.valueInExportFormat() is NSDictionary
{
// User is exits
}
else
{
}
}
In Objective c
[[[[[FIRDatabase database] referenceWithPath:#"users"] queryOrderedByChild:#"usernames"] queryEqualToValue:#"your User Name"] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot)
{
if ([snapshot.valueInExportFormat isKindOfClass:[NSDictionary class]])
{
// User is exits
}
else
{
}
}];

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.

Firebase iOS - Snapshot suddenly returning null when I know a value exists

Code that has been running fine for weeks suddenly is returning a null value when I call snapshot.value. Here is a picture of my DB:
My code is:
_ = ref.child("profiles").child((empID as!FIRDataSnapshot).value as! String).child("nickname").observeSingleEvent(of: FIRDataEventType.value, with: { (snapshot) in
print(snapshot)
self.names.append((snapshot).value as! String)
})
When I print the ref that I'm trying to make the snapshot from, I copy and paste the link into my browser and it correctly takes me to this screen:
So I have no idea why when I print snapshot it says
Snap (nickname) <null>
when there clearly is a value there. Then it throws an exception in the line where I try to append the nickname to a list. Does anyone know how this is possible? Especially since I don't recall changing anything to do with this piece of code in weeks. While I did not change the code recently, the only thing I've done is upload my app to TestFlight and had a few people download, could that possibly cause any of this somehow? I doubt this is the case because my code retrieves a value from another snapshot before this particular call successfully. Thanks!
There may be a different/better way to do this, but for your purposes, the following will work:
First, create a Profile model in a Profile.swift file, like so:
import UIKit
class Goal: NSObject {
var nickname: String?
var type: String?
}
Now, you can do this:
let profiles = [Profile]()
ref.child("profiles").child(empID).observeSingleEvent(of: FIRDataEventType.value, with: { (snapshot) in
if let dictionary = snapshot.value as? [String : AnyObject] {
let profile = Profile()
profile.setValuesForKeys(dictionary)
for item in dictionary {
if item.key == "nickname" {
print(item)
// do whatever logic you wanna do with item here - item will be set to the value of key "nickname"
}
}
}
})

Resources