I just tried to retrieve data from firebase for my project. For example, display the facebook user name in UILabel. I store the facebook user data like this
then retrieve the data using :
let ref = Firebase(url:"https://<app-name>.firebaseio.com/users/facebook:10207213459687665/name")
ref.observeEventType(.Value, withBlock: { snapshot in
self.facebookUserName.text = snapshot.value as! String
})
It works perfectly but it is pretty stupid by retrieving user name in a specific path because that could be different facebook user login.
I'm thinking like check the user is logged in and display their name or checking the currentUser or any smarter way to do this?
I am not sure how to do that.
There are 100 different ways to do this; here's a couple
users
user_id_0
facebook_id: facebook:10207213459687665
name: Nicholas
user_id_1
facebook_id: facebook:12346578912345689
name: Frank
in the above, you would query for the facebook_id you want, which will return the node and all of the child nodes (name, address, etc). The user_id_x is a Firebase auto-generated node name (guaranteed to be distinct)
ref.queryOrderedByChild("facebook_id").queryEqualToValue("facebook:12346578912345689")
.observeSingleEventOfType(.ChildAdded, withBlock: { snapshot in
print(snapshot.value) //prints the facebook_id and Frank
})
Another option is to use your same data structure and observe that node to load the data. Keep in mind that the facebook id is the KEY of the node, not the value - .value is key:value pairs.
let ref = Firebase(url:"https://<app-name>.firebaseio.com/users")
let thisUser = ref.childByAppendingPath("facebook:12346578912345689")
ref.observeSingleEventOfType(.ChildAdded, withBlock: { snapshot in
//snapshot will contain all of the child nodes
let userName = snapshot.value.objectForKey("name")
self.facebookUserName.text = userName
})
If you just care about the users name, you could simplify your structure by using the facebook id as the key and the value would be the user name:
users
fb_10207213459687665: Nicholas
fb_12346578912345689: Frank
and retrieve with the above observe code except you would again use the .value property as in your initial question.
In this case the .value property is a string as it's the only value (there are no child nodes as in the structure you posted, which could cause issues as it could be a series a key:value pairs which would crash)
Queries add some overhead so the observe is more efficient.
I have found an answer in http://www.appcoda.com/firebase/
When we are trying to fetch the data from current user add this code below to store the uid(when creating user account)
NSUserDefaults.standardUserDefaults().setValue(result ["uid"], forKey: "uid")
assign the variable:
var CURRENT_USER_REF: Firebase {
let userID = NSUserDefaults.standardUserDefaults().valueForKey("uid") as! String
let currentUser = Firebase(url: "\(BASE_REF)").childByAppendingPath("users").childByAppendingPath(userID)
return currentUser!
}
implement code for function:
<reference>.observeEventType(FEventType.Value, withBlock: { snapshot in
let currentUser = snapshot.value.objectForKey("username") as! String
<--your code-->
print("Username: \(currentUser)")
self.currentUsername = currentUser
}, withCancelBlock: { error in
print(error.description)
})
}
Then we could obsolete the ref in a direct path.
I had the same problem. To fix it, I created a variable that automatically references the user that's logged in:
class ViewController: UIViewController {
let UsersRef = Firebase(url:"https://<app-name>.firebaseio.com/users")
Then, under the #IBOutlet that you're trying to get the user data to show up in, write:
let LoggedInUser = UsersRef.childByAppendingPath("\(UsersRef.authData.uid)")
When you run the code, "UsersRef.authData.uid" is automatically replaced by the specific ID of the user currently logged in. This should create a direct path to the database info of the logged in user without having to manually enter the user's ID in the reference.
To retrieve data of a facebook user we can use the currentUser property of FirebaseAuth which returns a FIRUser which contains all the information about you user. After reading all the user data you can store it anywhere but for the sake of this example I have used UserDefaults.
import Firebase
func getUserInformation()
{
let user = Auth.auth().currentUser
if let user = user {
UserDefaults.standard.setValue(user.displayName!, forKey: "Username")
let uid = user.uid
UserDefaults.standard.set(uid, forKey: "user_ID")
let url = "http://graph.facebook.com/\(uid)/picture?type=square"
UserDefaults.standard.set(url, forKey: "ImageData")
}
}
Related
I've been thinking about this but I thought I'd post a question to get some more thinking power behind this or to see if this is even possible. I am grabbing multiple uid's and then want to take these uid's and append them to a child in my database and then add further data to them. Since they are uid's I can't access them separately which would be a easy firebase "update values" call, so how could I take this list of uid's and then add them to a child so they are their own separate children and then add values to them? I am just thinking about how I would set this firebase call to say "add each one of these uid's as its own child".
How I am getting the uid's
func getEmployees() {
let employees = Database.database().reference().child("Businesses").child(self.otherUser?["uid"] as! String).child("registered_employees").observe(.childAdded, with: { (snapshot) in
if snapshot.exists() {
let employess = snapshot.childSnapshot(forPath: "uid")
print(employess)
} else {
print("didnt call right values")
}
})
}
sample of data I would add to uid child
let userMessageRef = Database.database().reference().child("user-messages").child(fromID).child(toID)
let messageId = childRef.key
userMessageRef.updateChildValues([messageId!:1])
The code right above ^^^^ I would want the uid's to be in "toID" and then adding the "messageId" to those uid's
I don't know how I could even do each uid separately in the call because of the inability to extract each one and then set the data.
I think I understand so let me try an answer with an example. How we obtain the uid's we want to write is not outlined in the question so let try this:
Suppose we have a users node that stores our users and if they like pizza
users
uid_0 //the Firebase generated uid
name: "Richie"
likes_pizza: true
uid_1
name: "Marion"
likes_pizza: false
uid_2
name: "Fonzi"
likes_pizza: true
uid_3
name: "Howard"
likes_pizza: false
what we want to do it to get the users that like pizza, craft a new node and store each of the uid's as a parent and then a child of their name.
let usersRef = self.ref.child("users")
let pizzaQueryRef = usersRef.queryOrdered(byChild: "likes_pizza").queryEqual(toValue: true)
pizzaQueryRef.observeSingleEvent(of: .value, with: { snapshot in
guard let allUsers = snapshot.children.allObjects as? [DataSnapshot] else {return}
for user in allUsers {
let key = user.key
let name = user.childSnapshot(forPath: "name").value as! String
let pizzaRef = self.ref.child("pizza_lovers")
let aPizzaLoverRefUid = pizzaRef.child(key).child("their_name")
aPizzaLoverRefUid.setValue(name)
}
})
so this code queries for all users that like pizza (which enables us to access their uid's), and then (per the question) append them to a child in the database and then add further data to them
and then want to take these uid's and append them to a child in my
database and then add further data to them
the result is
pizza_lovers
uid_0
their_name: "Richie"
uid_2
their_name: "Fonzi"
Let me know if I misunderstood the question and I will update.
I am using Firebase database to store data. I am trying to snapshot every instance that a specific uid appears within a list. I am having trouble querying these ids.
CODE:
func reviews() {
let ref = Database.database().reference()
let user = Auth.auth().currentUser
let uid = user?.uid
ref.child("reviews").queryEqual(toValue: uid!).observe(DataEventType.childAdded, with: { (info) in
print(info) //prints null
})
}
Database:
What is the proper way of querying to show every instance of the uid (f7w0q6....)
If you're able to change the NoSQL modeling:
I would suggest to create an additional child, like user_review with the structure:
userUID
reviewID
reviewID
userUID
reviewID
...
Then you would query on that userUID and retrieve the values you want.
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.
When I create objects in Firebase, I use childByAutoId. How can I update these specific objects later? I'm having trouble obtaining the value of the key Firebase automatically updates. Snapshot.key just returns "users". Here's my JSON structure:
{
"users" : {
"-KQaU9lVcUYzIo52LgmN" : {
"device" : "e456f740-023e-440a"
"name: "Test"
}
},
How can I get the -KQaU9lVcUYzIo52LgmN key? I want to update the device child. Here's what I have so far. It currently creates a completely separate snapshot with a single child.
self.rootRef.child("users").queryOrdered(byChild: "name").queryEqual(toValue: self.currentUser).observeSingleEvent(of: .value, with: { (snapshot) in
let key = self.rootRef.child("users").childByAutoId().key
let childValues = ["device": device]
self.rootRef.child("users").child(key).updateChildValues(childValues)
Edit: device is a string set further up in the code. Not defined in this scope (to make it easier to read for this question).
When you get Snapshot.key, it returns "users" because that is the overall key for your snapshot. Everything inside of "users" in your snapshot is considered the value.
You need to iterate over the child layers to dig down to "device".
Try this:
rootRef.child("users").observeSingleEventOfType(.Value, withBlock: { (snapshot) in
if let result = snapshot.children.allObjects as? [FIRDataSnapshot] {
for child in result {
var userKey = child.key as! String
if(userKey == userKeyYouWantToUpdateDeviceFor){
rootRef.child("users").child(userKey).child("device").setValue(device)
}
}
}
})
This code will do the following:
Gets snapshot of your reference (the key for that would be
'users').
Gets all the children (your user keys) and assigns them as another
snapshot to 'result'.
Checks each key one at a time until it finds the key you want (for
example, if you look for user with the key "-KQaU9lVcUYzIo52LgmN",
it will find it on the first iteration in your example code you
posted).
Once it finds that key, it sets the value for the device inside that
key with the line
rootRef.child("users").child(userKey).child("device").setValue(device).
Of course, you will need to store all your user keys when you make them. You can maybe use SharedPreferences on the device for this, but if it gets cleared for any reason then that data will just be sitting there. You could also store it on internal storage for your app, but SharedPreferences is what I would use.
Hope this helps!
snapshot has a property key which is
The key of the location that generated this FIRDataSnapshot.
And as you can see you are getting one (snapshot) by calling observeSingleEvent(of: .value, with: { (snapshot)...
so instead of let key = self.rootRef.child("users").childByAutoId().key
try to call let key = snapshot.key
childByAutoId().key always generates new unique key based on timestamp, that's why you are creating new child, not updating the one you want
Hope that works
I adapted Ryan's answer to my own issue (kinda similar) and figured out a way to update your device ID directly without needed to know/store the AutoID key generated by Firebase :
reference = Database.database().reference().child("users")
reference.observeSingleEvent(of: .value, with: { (snapshot) in
if let result = snapshot.children.allObjects as? [DataSnapshot] {
for child in result {
if child.childSnapshot(forPath: "device").value as? String == self.yourDeviceIDVariable {
print("### Device exists in Firebase at key = \(child.key)")
let childKey = child.key
self.reference.child(childKey).child("device").setValue(yourNewDeviceID)
}
}
}
})
With the following code that is used to create a new user into Firebase BaaS, but when it is launched it overwrite the existing user.
let test = Firebase(url: "https://dr-freud.firebaseio.com/users")
ref.createUser(email.text, password: password.text,
withValueCompletionBlock: { error, result in
if error != nil {
print(error)
} else {
let uid = result["uid"] as? String
let nome = self.nome.text!
let cognome = self.cognome.text!
let utente = ["Nome": nome, "Cognome": cognome]
let users = ["\(self.nome.text!)": utente]
test.setValue(users)
}
})
You're calling setValue() on the same location, so you'll indeed overwrite the existing data at that location.
To prevent this, you'll need to call setValue() on a user-specific location:
let currentUser = test.childByAppendingPath(uid)
currentUser.setValue(users)
By calling childByAppendingPath() we end up with a reference to a database location that is specific to this user.
This and much more is covered in Firebase's programming guide for iOS. I highly recommend that you spend some time in that guide. A few hours there will save you many problems and questions down the line.
It's overwriting due to this line
test.setValue(users)
You need to create a separate child for each user that you write to the users node.
Since this is the users node, you should use the uid that the createUser provides. Frank's answer is correct so the below is more of a general case answer.
If it was some other node where you are not provided a natural key, using autoId will help you do this; here's an example.
let pizzaRef = ref.childByAppendingPath("pizzas")
let thisPizzaRef = pizzaRef.childByAutoId()
let pizzaData = ["crustType": "Thick"]
thisPizzaRef.setValue(pizzaData)
this will create a new node within the users node each time it's called
pizzas
-J1092uf8n0293i
crustType: "Thick"
-J989jijsoissds
crustType: "Thick"