Firebase overwrite existing record - ios

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"

Related

How to observe all firebase database events between two users at concurrent time?

First off, if you have a suggestion for a better title or actual question for this submission, please feel free to edit. I'm stuck as to how to succeed in asking this question.
So I've made gone through several Firebase chat (iMessage/ Facebook chat) tutorials for swift. I know how to send a message -
let ref = Database.database().reference().child("Message")
let childRef = ref.childByAutoId()
let toID = finalSelected.ContactID as Any
let fromID = Auth.auth().currentUser?.uid
let values = ["Message": messageTextField.text!, "toID": toID, "fromID": fromID!] as [String: Any]
childRef.updateChildValues(values) { (error, ref) in ...
and I know how to retrieve them -
let messagesOf = Auth.auth().currentUser?.uid
let messageDB = Database.database().reference().child("Message")
let userMessages = messageDB.queryOrdered(byChild: "toID").queryEqual(toValue: messagesOf)
userMessages.observeSingleEvent(of: .childAdded, with: { (snapshot) in
let values = snapshot.value as! Dictionary<String, String>
let message = values["Message"]
let from = values["fromID"]
let post = ChatMessages()
post.aMessage = message!
post.Interested = from!
self.messagesArray.append(post)
self.tableView.reloadData()
})
However, I'm having a difficult time finishing the logic. I don't understand how these two separate events combine into one identical end result - not two different transactions. Let me see if I can explain it further...
I send a message. I then receive this message as another user. But I do not understand how the to/from data is downloaded that references both simultaneously. Unless I'm looking overlooking some detail, doesn't the single or plural observation of an event only apply to one user? Or am I misunderstanding some concept here?
Help with this final concept would be fantastic. Thank You.
You now have a single list of chat messages, which is very similar to how you'd model this in a relations database. But Firebase is a NoSQL database, so you have better options for modeling chat.
The most common solution is to model chat rooms into your database. So if two users are chatting, then the messages between those two users will be in a "room node". And if two other users are also chatting, their messages will be in a separate room. The data model for this will look like:
chats: {
roomid1: {
messageid1: {
fromId: "UidOfSender1",
message: "Hello there user 2!"
},
messageid2: {
fromId: "UidOfSender2",
message: "Hey user 1. How are you?"
}
},
roomid2: {
messageid3: {
fromId: "UidOfSender2",
message: "Hi mom. Are you there?"
},
messageid4: {
fromId: "UidOfSender3",
message: "Hey there kiddo. Sup?"
}
}
}
So in here we have two chat rooms, the first one between user 1 and 2, and the second between users 2 and 3. If you think of your favorite messaging application, you can probably see how the rooms very directly map to the conversations you see.
Also note that you only need to keep the sender ID. The recipient(s) are are simply everyone else who is in this chat room.
That will likely be your next question: how do I know what rooms a user is in. To determine that, you'll want to keep a separate list of room IDs for each user:
userRooms: {
UidOfSender1: {
room1: true,
room2: true
},
UidOfSender2: {
room1: true
},
UidOfSender3: {
room2: true
}
}
So now you can see for each user in what rooms they are a participant. You can probably again see how this maps to the list of conversations when you start your favorite messaging app. To efficiently show such a list, you may want to keep some extra information for each room, such as the timestamp when the last message was posted.
Generating the room IDs is another interesting aspect of this model. In the past I've recommended to use the UIDs of the participants to determine the room ID, since that leads to a consistent, repeatable room ID. For an example if this, see http://stackoverflow.com/questions/33540479/best-way-to-manage-chat-channels-in-firebase.
If you're new to NoSQL database, I recommend reading NoSQL data modeling. If you come from a SQL background, I recommend watching Firebase for SQL developers.
var newthing = "\(CurrentChatUserId) & \(otherDude)"
Ref.child("posts").child(newthing).queryOrderedByKey().observe(.childAdded, with: {snapshot in
if let dict = snapshot.value as? [String: AnyObject]
{
let mediaType = dict["MediaType"] as! String
let senderId = dict["senderId"] as! String
let senderName = dict["senderName"] as! String
self.obsereveUsers(id: senderId)
let text = dict["text"] as! String
self.messages.append(JSQMessage(senderId: senderId, displayName: senderName , text: text))
self.collectionView.reloadData()
}
})
You could do something like this, so in the database you have different sections for each conversation and at the start get all past messages, and get new ones when a new one is added.

Firebase load related data iOS

What's the best way to load "related" data in swift?
Common setup, if I have a list of users all stored under uid node and contains a list of follows which stores uids, something like:
"users" : {
"abc123" : {
"email" : "test#test.com",
"follows" : {
"xyz789" : true
}
},
"xyz789" : { ... }
}
What's the most efficient way of loading in the data for all the users one user follows? Is it best to loop through each of the uid's with observeSingleEvent(of: .value)?
This is the solution I've come up with, but feels somewhat cumbersome:
func loadRelated(user: User, completion: #escaping (Bool, [UserObject]) -> ()) {
let ref = Database.database().reference(withPath: "users/" + user.uid + "/follows")
ref.observeSingleEvent(of: .value) { snapshot in
var uids = [String]()
for child in snapshot.children {
let userData = child as! DataSnapshot
uids.append(userData.key)
}
let userRef = Database.database().reference(withPath: "users")
var users = [UserObject]()
var count = 0
uids.forEach { uid in
userRef.child(uid).observeSingleEvent(of: .value) { snapshot in
let user: UserObject(from: snapshot)
users.append(user)
count += 1
if count == uids.count {
completion(true, users)
}
}
}
}
}
I don't really want to go down the denormalization path and store each users data under the top level user.
If you are decided on using Realtime Database, it is best practice to create another root node in your case called user-follows. You can create a follow at the path user-follows/$uid/$fid by setting the value to true, then on your app you would have to observeSingleEvent for each snapshot key ($fid) at user-follows/$uid.
To avoid having to observe each follow separately, instead of setting the value to true, you can just store the data you need about a user in user-follows/$uid. However, a user may change their username for example and so you would need to keep the data inside each user-follows up to date. You can utilise Firebase Cloud Functions to maintain the user-follows when a user changes their information.
Otherwise, I would suggest looking at Firebase Firestore, where some nesting is allowed.
If you know that your node at /users will always contain few users, you could try to get all the users at once with a observeSingleEvent(of:) at path /users. Then filter the users with the ones who are in ../follows.
This may pull more data but it might be faster (not sure) and will need less code to handle.
In fact your initial implementation is quite performant already. Just make sure to handle correctly failing of observeSingleEvent(of:) or the condition count == uids.count will never be fulfilled.
By the way storing each user under ../follows will just duplicate your data and will be hard to maintain updated. So yes avoid it.

Trying to let users save their profile img. but i keep getting cannot invoke 'user?' with no arguments.

trying to let users save their profile img to the storage
if Auth.auth().currentUser != nil {
profileImg.image = selectedImage
let user = User?()
let ref = Storage.storage().reference()
let imgsReference = ref.child("proimgs")
let uid = user?.uid
let newimgReference = imgsReference.child(uid!)
newimgReference.setValue(["proimgs": self.selectedImage.image!])
print("description: \"(newimgReference.description())") } }
(btw, sorry i'm very new to coding)
let user = User?(), it would appear that the User() intializer takes some sort of parameters. You did not provide enough context to provide a solution, just point you to the issue. Your code snippet doesn't provide any information on where this User class came from or what it's usage requirements are.
Edit:
On second look, perhaps it's actually the line let uid = user?.uid, and the issue is that user wasn't properly initialized since you used User?() instead of User(), and since you didn't specify a type for user it has no idea what the property uid is.
In your if statement, please try:
if let user = Auth.auth().currentUser {
...
}
and then use the "user" variable as you are.

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 + swift retrieving user data

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")
}
}

Resources