Showing post from currentUsers wall - Firebase / Swift - ios

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!!

Related

Accessing Specific key on Firebase (Swift)

I am trying to update child values within firebase.
User first will create a new order, it creates two nodes, one in the main orders section and second under user to keep clean records. this seem to have worked but I am struggling to update values
then while he is on the form and makes updates, I want firebase to update simultaneously on firebase. How do I access to that specific key as nothing seem to have worked for me when I tried using observe method.
What will be the best way to access that key that the form is on and update values?
This is how you can update values in Firebase:
func updateDatabaseForEdits() {
let updates : [AnyHashable: Any] = ["variableName": value,
"variableName2": value]
ref.child("COrders").child("specificKeyYouWantToEdit").updateChildValues(updates, withCompletionBlock: { (error, success) in
if error != nil {
// upload failed
return
}
else {
// upload worked
// update your locally stored values
}
})
}
There are other issues with you app though. Specifically, how you're storing the data. How do you expect to know which key to access? I recommend you update your data store to be something like this:
desikhanapeena {
COrder {
key123 {
orderInformation
}
}
UserOrders {
uid {
key123
orderInformation
}
}
}
Let me know if you have questions:
If you want to get the key from a snapshot you can do that like this:
for child in snap.children {
let child = child as? DataSnapshot
if let key = child?.key {
// print(key)
}
}
If you want to get the key before you upload you can do that like this:
let key = ref.child("COrders").childByAutoId().key
in case if you are still looking for answer.
Answer for Prob #1.
you need to update order value for user, if it successfull then take parent key (in your case , its AutoID) from there , and using this key, update in "Corders".
here is the code
func updateBothValue(){
let value = ["key":"data","key1": "data2"]
REF.child("Users").child(UID).child("UserCorder").childByAutoId().setValue(value){
error, ref in
guard error == nil else { return }
let key = ref.key
REF.child("Coorders").child(key).setvalue(value)
}
}
for Prob #2, as you written "access that key that the form is on". for each form data , store the key which you get from firebase in previous steps. and use this key for updates.

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)

Output order becomes different in nested query, using Firebase 3, Swift

I'm using Firebase to store user info, and I have this nested function that fetch the post info, and then using the UID in post to fetch the user info.
Nested function to fetch post info and then fetch user
func fetchUser(completion: #escaping (User) -> Void) {
REF_POST.queryOrdered(byChild: "timestamp").observe(.childAdded, with: { (postData) in
let post = ConvertPost(data: postData.key)
print(post.uid) >>>>>>UID ordered by timestamp<<<<<<<<
REF_USER.child(post.uid).observeSingleEvent(of: .value, with: { (userData) in
print(post.uid) >>>>>>UID order becomes different<<<<<<<<
let user = ConvertUser(data: userData)
completion(user)
})
}
I have a print(uid) before observing the users, the output is ordered by timestamp, which is what I want:
PXT6********
WT7i********
WT7i********
PXT6********
And a print(uid) inside observing users, the output order is different:
WT7i********
WT7i********
PXT6********
PXT6********
so my question is why the order becomes different?
I'm calling the method in ViewDidLoad()
Is it something to do with the closure block?
Question Update
After some testing, I found that the output will always group the same uid together, something like A,A,B,B,C,C. Please help me.
Use this code below:
func observeUsers(uid: String, completion: #escaping (User) -> Void) {
print(uid)
REF_USERS.keepSynced(true) // <-- this will make sure your code will update with fresh data
REF_USERS.child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
print(uid)
let user = ConvertUser(data: snapshot.value)
completion(user)
}
})
}
Either use that code, or disable data persistance in your appDelegate. More information:Firebase : What is the difference between setPersistenceEnabled and keepSynced? and in the docs of Firebase ofcourse.

Struggling to append arrays across Parse classes

Hi guys I'm trying to build a simple swipe app to like and dislike uploaded photos. I'm struggling with adding the likes/dislikes to Parse the way that I want them to. I've tried two ways so far:
adding the objectId of the posted image to the User who liked/disliked it but the problem is only one of the objectId's shows up in the array.
staying in the Parse class where the images are posted to (Post), add the userID of the liker/disliker to the image. This doesn't happen at all, new rows are created with new objectId's everytime an image is liked/disliked.
Ideally I want the users who have liked/disliked the photo in a single array so I can query this later. I don't have a great understanding of Parse, it's my first time using it so any help will be massively appreciated.
Here is the code I'm using when an image is swiped (adding to Post class):
if gesture.state == UIGestureRecognizerState.Ended {
var likedOrDisliked = ""
if label.center.x < 100 {
print("Dislike")
likedOrDisliked = "disliked"
} else if label.center.x > self.view.bounds.width - 100 {
print("Like")
likedOrDisliked = "liked"
}
if likedOrDisliked != ""{
var post = PFObject(className: "Post")
post.addUniqueObjectsFromArray([(PFUser.currentUser()?.objectId!)!], forKey: likedOrDisliked)
post.saveInBackground()
}
This is the snippet of when I try adding to User class:
PFUser.currentUser()?.addUniqueObjectsFromArray([displayedUserID], forKey: likedOrDisliked)
do {
try PFUser.currentUser()?.save()
} catch {
}
Here is what happens in the dashboard,
new rows created
What you wanted is to update the actual Post with the like/dislike user
Create a Post (This part you have not explained but i am show a simple assumption - pseuodo code)
var post = PFObject(class:"Post")
post["image"] = PFFile(image)
post.save()
Next you show the image on screen by getting the image from the post
When the user dislikes/likes
you add the current PFUser to the liked/disliked column and save back the object.
let arrayMut = NSMutableArray()
var array = NSArray()
if let arrayData = post.objectForKey("likedUser") as? NSArray {
array = arrayData
}
loop through now the array to find if current user is there.. if not find .. add current PFUser
arrayMut.addObject(PFUser.currentUser().objectId);
post.setObject(arrayMut, forKey: "likedUser")
post.save()
I've tried a lot of things and eventually something stuck, the desired effect was achieved through (added the current user to the liked or disliked fields) :
if likedOrDisliked != ""{
var post = PFQuery(className: "Post")
post.findObjectsInBackgroundWithBlock({ (objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects {
for object in objects {
var objId = object["objectId"]
var query = PFQuery(className: "Post")
query.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if error == nil {
object.addUniqueObjectsFromArray([(PFUser.currentUser()?.objectId)!], forKey: likedOrDisliked)
object.saveInBackground()
}
})
}
}
}
})

Retrieving data with .Value and .ChildAdded

I have a list of data to retrieve from Firebase, using Swift.
I tried to get the data using .Value and Firebase returns a dictionary with the IDs of each item and for each ID the info associated.
The endpoint I am calling is /ideas.
let ideasRef = firebase.childByAppendingPath(IdeaStructure.PATH_IDEAS)
ideasRef.observeEventOfType(.Value, withBlock: { snapshot in
print(snapshot.value)
}, withCancelBlock: { error in
print(error.description)
})
In order to optimize this, I changed with .ChildAdded. In this case I get only the single item without the ID associated.
Is it possible to get also the ID of each item using .ChildAdded?
If not, how can I save the ID generated by Firebase into each item? Currently I am saving each item in this way:
let ideasRef = firebase.childByAppendingPath(IdeaStructure.PATH_IDEAS).childByAutoId()
let idea = [
IdeaStructure.FIELD_MESSAGE: message,
IdeaStructure.FIELD_CREATOR_ID: userId,
IdeaStructure.FIELD_CREATION_DATE: NSDate().formattedISO8601
]
ideasRef.setValue(idea)
To get the key of the snapshot, access its key property:
let ideasRef = firebase.childByAppendingPath(IdeaStructure.PATH_IDEAS)
ideasRef.observeEventOfType(.Value, withBlock: { snapshot in
print(snapshot.key)
print(snapshot.value)
}, withCancelBlock: { error in
print(error.description)
})
This and many more topics are covered in Firebase's excellent programming guide for iOS.

Resources