Update a particular subchild in firebase realtime database swift 4 - ios

The screenshot above is of the database i created. I successfully am able to upload and retreive data through code but i dont know how to update a subchild. For instance if i want to update the subchild user_name how can i acheive this in swift any snippet would be appreciated.

ref.child("USERS").child(email).child("user_name").setValue("new user name"){
(error:Error?, ref:DatabaseReference) in
if let error = error {
//error
} else {
//do stuff
}
}
}

It's simple, you need just call setValue on the child like this:
ref.observeSingleEvent(of: .value, with: { (snapshot) in
self.ref.child("USERS").child(email).child("user_name").setValue("new User Name")
})

Related

Query Firebase Database With Swift

I am trying to query an entry in my firebase realtime database with Swift to basically see if an entry exists with the owner = 33206. My current code is not returning what I need, how can I fix this query?
Here is my code:
var promoCodes: DatabaseReference {
return ref.child("PromoCodes")
}
func getIsAffiliate() {
promoCodes.child("owner").queryEqual(toValue: 33206).observeSingleEvent(of: .value) { snapshot in
print("SNAP HERE: \(snapshot)")
AffiliateService.isAffiliate = snapshot == nil
}
}
Output: SNAP HERE: Snap (owner) <null>
The snapshot passed to your code will never be nil. To check if there are matching nodes, you can check snapshot.exists() instead.

How to know which subchild is updated in childChanged eventtype of realtime firebase in swift 4

Assuming My Firebase RealTime Database has the child name Users which further has sub-child which are emails like this "christianLiz#gmail.com", the sub-child emails further have sub-child called name, age, and country.
I added an observeEvent on child changed the snippets looks like this.
ref.child("USERS").child("christianLiz#gmail,com").observe(.childChanged, with: { snapshot in
if snapshot.exists(){
let value = snapshot.value as? NSDictionary
if value!["name"] != nil{
print("here")
}else{
print("not here")
}
}){ (error) in
print("")
}
When i test after changing the value of country the app crashes and cause is this value!["name"]. Now i do know that the childChanged event is only returning the child which is changed in my testing scenario "country" so other values are notin snapshot but i want to check which one is updated name or age or country. How can i modify the code above to achieve this.
Use hasChild to check if a child exists or not:
if snapshot.hasChild("country"){
print("found")
} else {
print("not found")
}

Swift Firebase Delete Post

I need help deleting a post from firebase. I have the post id key and everything and have been trying all the methods and nothing works. I want to delete the the 3rd child in "posts". I have been trying all types of code. The code below doesn't even throw an error. Not sure what to do. I am getting the values by using a indexpath.row tag.
let postID = self.posts[sender.tag].postID
Database.database().reference().child("posts").child(uid!).child(postID).removeValue { error, _ in
if error != nil {
print("error \(error)")
}
}
Testing
I tried this, and this deletes everything under that user...
Database.database().reference().child("posts").child(uid!).queryOrdered(byChild: "postID").queryEqual(toValue: postID).observe(.value, with: { snapshot in
if snapshot.exists() {
snapshot.ref.removeValue()
print(snapshot)
} else {
print("snapshot doesn't exist")
}
})
check the value of the reference you want to delete by
Database.database().reference().child("posts").child(uid!).child(postID).observe(.value, with: { snapshot in
print(snapshot.value)
})
you can also put them together to check if it's happening locally or not
Database.database().reference().child("posts").child(uid!).child(postID).removeValue()
Database.database().reference().child("posts").child(uid!).child(postID).observe(.value, with: { snapshot in
print(snapshot.value)
})
if it's correct you may don't have access to delete node from the database, check database rules tab in the firebase console
as a last resort you may check your connection because if there is slow connection firebase will cache the updates you make until there is a good connection to sync to the cloud
This works.
Database.database().reference().child("posts").child(uid!).queryOrdered(byChild: "postID").queryEqual(toValue: postID).observe(.childAdded, with: { snapshot in
if snapshot.exists() {
snapshot.ref.removeValue()
print(snapshot)
} else {
print("snapshot doesn't exist")
}
})

Firebase Child Added Is Called Every time a Child is Changed

I have a group messaging application that works fine until I want to change some of the basic group properties such as group title, image, etc. Before I show my code to display my conversations and update them I will show you some of my data structure.
When it comes to dealing with the displaying and editing of conversations I use two main nodes. An overall conversation node containing the conversation properties and a conversations node within my current user.
Here is what the conversation node in my current user looks like:
As you can see in the image above my user has a conversation node with a list of conversation ids. These conversation ids refer to a conversation node within my database. Here is a picture of the conversation node:
Just to review the problem. Basically when I update any of the conversation properties (title, image, members) it re calls my child added method which creates an error I will show later.
Here is my code to display the conversations:
func observeUserConversations() {
guard let uid = currentUserProperties.id else {
return
}
FIRDatabase.database().reference().child("users").child(uid).child("conversations").observe(.childAdded, with: { (snapshot) in
FIRDatabase.database().reference().child("conversations").child(snapshot.key).observe(.value, with: { (conversationSnapshot) in
if let conversation = Groups(snapshot: conversationSnapshot) {
conversation.groupId = conversationSnapshot.key
self.conversations.append(conversation)
DispatchQueue.main.async(execute: {
self.tableView.reloadData()
})
}
}, withCancel: nil)
}, withCancel: nil)
}
Here is my code to update some of the conversation properties:
static func updateConversationProperties(conversationId: String, property: String, propertyValue: String) {
let updateConversationPropertyRef = FIRDatabase.database().reference().child("conversations").child(conversationId).child(property)
updateConversationPropertyRef.setValue(propertyValue)
ProgressHUD.showSuccess("Field Updated!")
}
Please note I have tried using update child values instead of set value and it still has the same bug.
To sum up whenever I update a conversation property the child added function is called and appends a duplicate version of the conversation to my conversation array.
I know this may be a bit confusing, so I have a video here showing the bug:
https://youtu.be/OhhnYzQRKi8
In the video above you will see that the same conversaiton is duplicated and added twice.
Any help would be appreciated!
UPDATE
So I changed my observers a bit to look like this:
FIRDatabase.database().reference().child("users").child(uid).child("conversations").observe(.childAdded, with: { (snapshot) in
FIRDatabase.database().reference().child("conversations").child(snapshot.key).observeSingleEvent(of: .value, with: { (conversationSnapshot) in
if let conversation = Groups(snapshot: conversationSnapshot) {
conversation.groupId = conversationSnapshot.key
self.conversations.append(conversation)
DispatchQueue.main.async(execute: {
self.tableView.reloadData()
})
}
}, withCancel: nil)
}, withCancel: nil)
In the above code, everything works and no duplicates are made. However, now the conversations won't update in realtime. Instead they will display the old data and won't update to the newly changed data. Also if I add a conversation the new added conversaiton won't display.
Here is what I notice:
The way you had the code originally, the second listener was triggered any time a change is made to the value of /"conversations"/snapshot.key. And whenever this call was made, you were appending the conversationSnapshot to conversations array:
FIRDatabase.database().reference().child("users").child(uid).child("conversations").observe(.childAdded, with: { (snapshot) in
FIRDatabase.database().reference().child("conversations").child(snapshot.key).observe(of: .value, with: { (conversationSnapshot) in
if let conversation = Groups(snapshot: conversationSnapshot) {
conversation.groupId = conversationSnapshot.key
self.conversations.append(conversation) // here is where you are appending the data. This will be appended each time a change is made
DispatchQueue.main.async(execute: {
self.tableView.reloadData()
})
}
}, withCancel: nil)
}, withCancel: nil)
Now as you point out, if you change FIRDatabase.database().reference().child("conversations").child(snapshot.key).observe to .observeSingleEvent, the data won't append again, but you won't get updates. One option is whenever the listener is triggered, you search the array for the snapshot key, and then update the snapshot at that index if found. Not the most efficient method, to be sure.
In summation, it sounds like you do need to use observe, but as it stands, the data is duplicated because the code appends the snapshot to the end of the array whenever a change is made to the snapshot's value. You will have to use something other than self.conversations.append(conversation).
I'd be happy to brainstorm some other options if you wanted to message me directly.
FIRDatabase.database().reference().child("users").child(uid).child("conversations").observe(.childAdded, with: { (snapshot) in
FIRDatabase.database().reference().child("conversations").observe(.childAdded, with: { (conversationAdded) in
if conversationAdded.key == snapshot.key {
if let group = Groups(snapshot: conversationAdded) {
self.conversations.append(group)
DispatchQueue.main.async(execute: {
self.tableView.reloadData()
})
}
}
})
}, withCancel: nil)
FIRDatabase.database().reference().child("users").child(uid).child("conversations").observe(.childAdded, with: { (snapshot) in
FIRDatabase.database().reference().child("conversations").child(snapshot.key).observe(.childChanged, with: { (conversationSnapshot) in
let conversationIdsArray = self.conversations.map({$0.groupId})
let changeAtGroupIdIndex = conversationIdsArray.index(of: snapshot.key)
let conversationToBeUpdated = self.conversations[changeAtGroupIdIndex!]
conversationToBeUpdated.setValue(conversationSnapshot.value, forKeyPath: conversationSnapshot.key)
self.tableView.reloadData()
}, withCancel: nil)
}, withCancel: nil)
In the above code, I create two different observers. The first one loads conversations when the app is loaded or a conversation is added. The second one updates the conversation array if the child has been changed. This solves both problems.
Filter array with data excluding the object just received. Identify that object in the existing array by a unique id like groupID or chatID in my case. Then the repeated object will be removed
self.conversations = self.conversations.filter { obj in (obj.chatId as? String) != (data.chatId as? String) }
self.conversations.append(data)

Showing post from currentUsers wall - Firebase / Swift

I am trying to create a wall/timeline that shows posts from all the users that currentUser is following. All users that currentUser is following is showed under Users -> UserID -> Following. Whenever one of their followers is making a post it will be added under feed-items with an autoID - the key (the autoID) is added to currentUsers Users -> UserID -> Wall at the same time.
Here is an image of an example from my Firebase database:
Under Wall as you can see, one of this users followers has made a post (the whole post is saved under feed-items) and the autoID of that post has made it to the users Wall.
Now I am trying to figure out how to show all the posts in feed-items, based on the autoID's stored under currentUsers Wall.
I have tried the following code, but nothing shows and when it reaches this line print(self.updates.count) it is printing 0.
func startObersvingDB(userID: String) {
FIRDatabase.database().reference().child("Users").child(userID).child("Wall").observeEventType(.ChildAdded, withBlock: { snapshot in
if let posts = snapshot.value!["Post"] as? String {
self.postArray.append(posts)
for i in 0..<self.postArray.count {
let post = self.postArray[i]
print(post)
FIRDatabase.database().reference().child("feed-items").queryEqualToValue(post).observeEventType(.Value, withBlock: { (snapshot: FIRDataSnapshot) in
var newUpdates = [Sweet]()
for update in snapshot.children {
let updateObject = Sweet(snapshot: update as! FIRDataSnapshot)
newUpdates.append(updateObject)
}
self.updates = newUpdates.reverse()
print(self.updates.count)
self.tableView.reloadData()
}) { (error: NSError) in
print(error.description)
}
}
}
})
}
If I am guessing right, your structure for feed-items is something like this.
feed-items
-UniquePostID
-Post Data (key-value pair(s))
If this is the case then to retrieve data for a post use .child(post) instead of .queryEqualToValue(post). Also since this will return DataSnapshot for single post you can directly create your Sweet object and append it in existing updates array.
One more thing I don't think you need to iterate entire postArray each time a new post is added. You should retrieve data for new post only.
Hope this helps!!

Resources