Handling User Likes
Instagram Firebase
One great feature that belongs in any Social Media application is the ability to like things. In this application, let's look at how you can add a like for each post in Firebase Database and have our UI reflect this change. Things can become a bit hairy if you include a cache, so we'll just leave the implementation simple by fetching the like status each time we need it.

Comments (35)
Jesus Adolfo
6 years ago
One video left :'(
Brian Voong
6 years ago
omari
6 years ago
Hey Brian, this tutorial is amazing. If you find time please answer to some of my comments. It would be nice to see some more important features that are missing :) like hashtags, edit profile, pagination etc. If you expand that course this would give us the most value instead just start another app tutorial. Learning main features through that course is the best way to learn. We waiting and again Thanks a lot Brian !!!
Razzor Owa
6 years ago
It was REALLY fast :(
omari
6 years ago
Hello Brian, isn't it better due to performance reason instead of saving a 0 into the database for disliking to delete the node =
omari
6 years ago
Hey Brian, Can you show us how to count the amount of likes and comments and to display them. If a user dislikes or deletes his comment it should change the value in realtime and every user shall see it. Also the labels in the UserProfileController with follower, following and posts are empty .
omari
6 years ago
Hey Brian, you can like the posts and comment posts also if you click outside of icons. How can we fix that bug. ?
Razzor Owa
6 years ago
Hey, Brian, I tried to implement the "heart popping up" behaviour when you double click the PhotoImageView. I did it using delegation but I have a intermittent problem. When I double click que PhotoImageView the heart animation functions sometimes only... :(
weare99
6 years ago
You have a nice car how much ps has your gti ? :) Do you think to buy an Tesla model 3 :) ? best regards from Germany
omari
6 years ago
Hey Brian, what do you mean by :" things can become a bit hairy if you include a cache, so we'll just leave the implementation simple by fetching the like status each time we need it." Why do we have to include a cache and what isn't so good on the current solution ? Is it because we always making Firebase calls to retrieve the information whether a post its liked etc.. Can you please show us the way with the cache.
shender ramos
6 years ago
hey brian are you going to add the notification controller to get notify when an user likes or comment.. etc ?
Sid
6 years ago
I have a question about the way to get post for a particular cell. guard let indexPath = self.collectionView?.indexPath(for: cell) else { return } var post = posts[indexPath.item] may I ask the reason why not just do cell.post?
Brian Voong
6 years ago
Ryan Temple
6 years ago
Would storing "Likes" on Firebase DB as a property of a Post, rather than its own node, be more efficient than storing a list? I'm very new to this topic of storing data and it seemed like it would make sense to store Likes within the Post object. It could be a list of users who liked the current photo? Which one will be more scalable in the long term?
Brian Voong
6 years ago
Jesus Miguel Rosado Perdomo
6 years ago
Hi from london UK Brian, man you're a legend thanks a lot for you video tutorails they help a lot and I learned a lot, thank you... One thing if you could please help me with is that I added a UILabel (likes counter ) to the homeCountroller but I'm having problem retrieving the value to show the number of likes of you could just show me how can I add that function I'll appreciate it Thanks in advance also I'll like to include videos to home controller how can we achieve this??? Thanks and hope you're having a greate weekend... Kind regards Jesus Miguel
Thanh Minh
6 years ago
cell.post = posts[indexPath.item] // print("PostId: \(cell.post?.postId)") let postId = cell.post?.postId firebase.child("Comments").child(postId!).observe(.value, with: { (snapshot) in guard let dictionaries = snapshot.value as? [String:Any] else { return } print("Comment count: \(dictionaries.count)") cell.commentNumLbl.text = "\(dictionaries.count)" })
stonypig1
6 years ago
firebase dedicated a method to run Like count or any count, because sometimes user loss cell data, when they grab the snapshot LIKE value, once cell data is up they reconnct to firebase , even a few seconds, it will cause huge problem. say your post had 0 likes, you delay 3 seconds , 300 people already liked it, but your snapshot value still 0, so once you recover from data lose, you update the value to 1, haha. i tried the array.count, it works but i don't think it is effecient way to do it. say you got 3000 likes or more, you need to to run entire array to get the count every time ? try this: mydataRef.runTransactionBlock({currentdata} -> TransactionResult in this method will recheck your firebase data integrity, once you recover from your data lose, once it is reverified, you send your new data again. secure and fast , btw i am a newbie, just on this subject i dig sometime on it, if there is something wrong, please share. thanks
jonathanking
6 years ago
Hey, I'm having a hard time finding where you set up the Edit Profile page?
stonypig1
6 years ago
hello guys, i try to build a similar one but add the like counting number next to like heart image. it works, i tap heart go red, and number go +1, and i tap again heart go white and number -1, but i just realize an error, if i scroll tableview down and up say 5 times, once i tap the like button, it is called 5 times !!!!!! i try to solve it and search the google for 6 days now. no one seems have the similar problem, do you guys think this is the problem come from dequed cell not being removed ? please give some advice or any link may help , thanks
stonypig1
6 years ago
repeating code happens at the observ .value of like count value inside firebase, but if i change it to the observeSingleEven.(of: .value) , the repeat problem solved, but the like count number won't change if i tap the heart instantly ? this is the dilemma i am running to, i think my concept about these firebase and tableview are still blur, i can't figure out where is the problem, anyone please give me a hint, thanks.
oalansari82
5 years ago
Hi Brian, So after getting this to work, I wanted to pull the user's likes and show it in a view. The way I went with was to save another child into the database as "userLikes" where I am saving UIDs of users and then posts IDs below it. Having done that it seems like I would also need to save the posts data separately in order to retrieve the full data of posts. I feel that this is just duplicating information into the database which already exist. How would you go about that?
Arvid Schneider
5 years ago
Would it not make sense to store FirAuth.auth()?.currentUser?.uid in a global variable, instead of querying the db every time?
Brian Voong
5 years ago
Arvid Schneider
5 years ago
Good to know! Thanks man
Jeffrey Chang
5 years ago
Hi Brian, I have a question on where to store like in firebase. Since we have the possibility that multiple users might like the same photo, shouldn't we use firebase transaction?
Brian Voong
5 years ago
Jeffrey Chang
5 years ago
thank you, i look forward to the rest of your core data course :)
Kwesi Adu Cobbina
5 years ago
Can we see the code please.
Jaylon22
4 years ago
Hey Jeffery, I am currently working with displaying the likes count with Transaction and it is going very well. I was able to get a snapshot of if the user like the post or not the only thing i because we are returning likes of the logged in user. I can't retrieve anyone else likes. Here the code: likesRef.runTransactionBlock({ (likes) -> TransactionResult in if let likesNum = likes.value as? Int, likesNum == 1{ print("Returning likes") print("Likes: " + String(likesNum)) return TransactionResult.success(withValue: likes) }else{ return TransactionResult.success(withValue: likes) } }, andCompletionBlock: {(error,completion,snap) in if !completion { print("The value wasn't able to Update") }else{ //Updated } }) It returns the post he likes and even gives it a count of one but the thing is i just can't access the others who like the post. Any tips? Thank you and have a bless day.
Kwesi Adu Cobbina
5 years ago
hello Brain, so for the like why do you add a number to it so users can see the number of likes a post has.
omair_34
5 years ago
There's a problem here .. if someone has liked a photo and some other user likes that same photo .. the previous like get overwritten by the new user.
yannsonnboys
5 years ago
Hello omar_34, I test what you say but the previous like is not overwritten. You should check again your code. It woks just fine
timSD
5 years ago
Hi Brian, I am unsure if this is just my computer but at 6:08 the video gets a playback error. I have refreshed multiple times and have gone on to complete the next video in the series with no similar problems. Just thought it should be brought to your attention. Thanks again for all that you do! Thanks, Tim
Brian Voong
5 years ago
Brian Voong
5 years ago
timSD
5 years ago
Hi Brian, Thanks for the response. My first attempt was on chrome. I just tried on safari and got the same result at the same time mark. Hope you have a good rest of your Monday! Best, TIm
timSD
5 years ago
I tried on my phone and the video works fine. Must be my comp. Sorry for bugging you. Thanks
Drew Pasma
5 years ago
YOU'RE A LEGEND BRIANVOONG
stonypig1
5 years ago
at 18:58, can you explain little more detail about when the post is outside an array, we will get different reference of that object ? but they are in the same function, why need to assign again? thanks
rehan1531
5 years ago
In my knowledge, it has to do with "COPY BY VALUE" vs "COPY BY REFERENCE" , in swift "struct" is always copied by value so when you write this statement "var myPost = posts[indexPath.item]" , you get a copy of the post at this index to your variable "myPost", so now if you make any changes to "myPost" e.g "myPost.hasLiked = true" , it only makes the change in the copy i.e "myPost" not the oroginal post inside the "posts" array. Hence you need to reassign this copy to the array at that indexpath so your changes can reflect when the collection view reloads. Further FYI, If we would have defined post as class , then we would not need the above procedure since class is always "copied by reference" means you would be making changes to the original post which resides in the array even if you do this "myPost.hasLiked = true" . Hope this helps. :)
aps17
5 years ago
Hi Brian, when i like or dislike a post. Instead of refreshing the same cell my code is creating a new cell right under the initial one. I compared it to your code but figure out the problem. I am stuck on it from quite a bit. Can you please help? Thanks
aps17
5 years ago
If i refresh home controller than the duplicated cell goes away
Brian Voong
5 years ago
aps17
5 years ago
I am not creating a duplicate cell. Thats what the problem is. I have the exact same code as yours but when I like a post, it seems to add a new cell with updated like heart and the old cell remains as such. If i like it one more time. It will create two duplicate cells, third time three and so on. The content of the posts variable is correct. It just seems like the refresh isnt working as its supposed to be. This is the didLike function that I have func didLike(for cell: HomePostCell) { guard let indexPath = collectionView?.indexPath(for: cell) else { return } var post = self.posts[indexPath.item] guard let postId = post.id else { return } guard let uid = FIRAuth.auth()?.currentUser?.uid else { return } let values = [uid: post.hasLiked == true ? 0 : 1] FIRDatabase.database().reference().child("likes").child(postId).updateChildValues(values) { (err, _) in if let err = err { print("Failed to like post:", err) return } print("Successfully liked post.") post.hasLiked = !post.hasLiked self.posts[indexPath.item] = post self.collectionView?.reloadItems(at: [indexPath]) } }
Brian Voong
5 years ago
allanaraujo
5 years ago
hey man. Saw this same issue. Inside of fetchPostsWithUser don't move the sorting and collectionView.reload data call to within that foreach statement. I left that bit of code out and it stopped creating duplicates. fileprivate func fetchPostsWithUser(user: User) { let ref = Database.database().reference().child("posts").child(user.uid) ref.observeSingleEvent(of: .value, with: { (snapshot) in self.collectionView?.refreshControl?.endRefreshing() guard let dictionaries = snapshot.value as? [String: Any] else {return} dictionaries.forEach({ (key, value) in guard let dictionary = value as? [String: Any] else {return} var post = Post(user: user, dictionary: dictionary) post.id = key guard let uid = Auth.auth().currentUser?.uid else {return} Database.database().reference().child("likes").child(key).child(uid).observe(.value, with: { (snapshot) in if let value = snapshot.value as? Int, value == 1 { post.hasLiked = true } else { post.hasLiked = false } self.posts.append(post) }, withCancel: { (err) in print("failed to fetch like info for post: ", err) }) }) //sort by date self.posts.sort(by: { (p1, p2) -> Bool in return p1.creationDate.compare(p2.creationDate) == .orderedDescending }) self.collectionView?.reloadData() }) { (err) in print("failed to fetch posts", err) } }
azhar
4 years ago
This fix doesnt work now unfortunately. Would like to also understand the logic behind it.
azhar
4 years ago
Found the fix. Check your firebase call it should be Database.database().reference().child("likes").child(key).child(uid).observeSingleEvent(of: .value, with....... If its Database.database().reference().child("likes").child(key).child(uid).observe(.value, with.... Then that's the problem. It's observing for all posts and hence adding an extra post instance to create an extra cell
Jorge Casariego
4 years ago
In my case this was the solution! Thanks
Jorge Casariego
4 years ago
Update: This change caused me some errors when I clicked the "like" button. Finally investigating the data in Firebase I realized that I was following myself, which caused my data to be repeated.
turtle0001
5 years ago
When I try to like my own post and reload by pulling down, the like icon is in unselected state but Firebase Database works fine. Even running the app again though your own post has a value of 1, icon still unselected. How is that?
AlfieLBTA
5 years ago
Hey Brian and fellow readers: im trying to extend this function to have a double tap functionality like instagram's but i can't seem to make it work inside the post picture, i can on the homecell tough, the code is as follow's , any pointer in the correct direction will be much appreciated. lazy var photoImgView: CustomImageView = { let iv = CustomImageView() iv.backgroundColor = UIColor(hex: AppColors.mainFontReverse.rawValue) iv.contentMode = .scaleAspectFill iv.clipsToBounds = true iv.isUserInteractionEnabled = true let doubleTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTap)) doubleTapGesture.numberOfTapsRequired = 2 iv.addGestureRecognizer(doubleTapGesture) return iv }() @objc fileprivate func handleDoubleTap(gesture: UITapGestureRecognizer){ if gesture.state == .ended { handleLike() } }
Goldor
4 years ago
Try putting self.handleDoubleTap in the selector instead. I had the same issue before, where the tap gesture recognizer didn't work until I added the "self." for the selector function, frustrating.
Junior2
5 years ago
how to create like / dislike for android studio please help me, or send me a source code of a project that has already done please
Jaylon22
5 years ago
Hey brian, Love the courses i just have a problem with the likes, i keep getting index out of range and i don't know why, could you please help me, here is my code. override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return posts.count } override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! EloheSplashCell cell.post = posts[indexPath.item] cell.delegate = self return cell } func didTapComment(post: Post) { print("Message coming from HomeController") print(post.caption) let commentsController = CommentsController(collectionViewLayout: UICollectionViewFlowLayout()) commentsController.post = post navigationController?.pushViewController(commentsController, animated: true) } func didLike(for cell: EloheSplashCell) { guard let indexPath = collectionView?.indexPath(for: cell) else { return } var post = self.posts[indexPath.item] print(post.caption) guard let postId = post.id else { return } guard let uid = Auth.auth().currentUser?.uid else { return } let values = [uid: post.hasLike == true ? 0 : 1] Database.database().reference().child("likes").child(postId).updateChildValues(values) { (err, _) in if let err = err { print("Failed to like post:", err) return } print("Successfully liked post.") post.hasLike = !post.hasLike self.posts[indexPath.item] = post self.collectionView?.reloadItems(at: [indexPath]) } } } Thank you and how a bless day
Jaylon22
4 years ago
Hey Brian, I am a little confuse about the Firebase transaction. I looked on Google, stackoverflow etc. And for the firebase website they gave this example for the likes counter. The only thing i am confuse with is how will i be able to access the database to store the likes count or maybe im doing it wrong? Thank you brian and you have a bless day. function toggleStar(postRef, uid) { postRef.transaction(function(post) { if (post) { if (post.stars && post.stars[uid]) { post.starCount--; post.stars[uid] = null; } else { post.starCount++; if (!post.stars) { post.stars = {}; } post.stars[uid] = true; } } return post; }); }
Jaylon22
4 years ago
Hey Brian, I love your videos and seriously don't mean to bother you today. I am currently working with displaying the likes count with Transaction and it is going very well. I was able to get a snapshot of the if the user like the post or not the only thing is because we are returning likes of the logged in user. I can't retrieve anyone else likes. Here the code: likesRef.runTransactionBlock({ (likes) -> TransactionResult in if let likesNum = likes.value as? Int, likesNum == 1{ print("Returning likes") print("Likes: " + String(likesNum)) return TransactionResult.success(withValue: likes) }else{ return TransactionResult.success(withValue: likes) } }, andCompletionBlock: {(error,completion,snap) in if !completion { print("The value wasn't able to Update") }else{ //Updated } }) It returns the post he likes and even gives it a count of one but the thing is i just can't access the others who like the post. Any tips? Thank you and have a bless day.
Jaylon22
4 years ago
Hey Brian and Everyone, I used this code to figure out how many likes a post have however i can't get it to display for the cell however, i was able to display it in the database. If there are any "bad code syntax" that i am using, please let me know. let likesRef = Database.database().reference().child("likes").child(postId) likesRef.updateChildValues(values) { (err, _) in if let err = err { print("Failed to like post:", err) return } print("Successfully liked post") post.hasLike = !post.hasLike likesRef.child("likes").runTransactionBlock({ (currentData:MutableData!) in var value = currentData.value as? Int if (value == nil) { value = 0 } if(!post.hasLike){ currentData.value = value! - 1 return TransactionResult.success(withValue: currentData) }else{ currentData.value = value! + 1 } return TransactionResult.success(withValue: currentData) }) You should see the count in the database, also brian or anyone if you could please help me find out a way to display the count. Thank you have a bless day.
Clint Larenz Nurse
4 years ago
Did you ever figure out how to display the like count?
Bar Korteran
4 years ago
Hey, Can you show how to reflect the change of the like button inside the post cell when you go between tabs\user's profile that posted? Also can you show how to do it with likes count? thanks.
Jie Hui
4 years ago
Hey Brian, May I know what is the purpose of having a 'for' in front of the 'cell' in didLike function's definition? As I have tried using just 'cell' alone in the definition, it is working out fine so I was wondering if the for is used for some purposes like preventing the app from crashing? Thanks.
Brian Voong
4 years ago
Jie Hui
4 years ago
Thank you for the clarification. By the way, Brian, is it possible for a developer to have some sort of a mechanism to prevent the user from using the old version of the app (like maybe the user has to get the latest version to access the features of the app)?
malrhex
4 years ago
Men, I have a great headache with this. I have more than 4 days 16h a day to show up the bookmark. I am able to save it on firebase even show it on the UI as a bookmark (dark color). well the thing is that I want to show it to the bookmark on UserProfileController and I am calling a method fetchBookMark! I am saving the bookmark the same as like node. Meaning that it has .child("bookmark").child(postId).child("userId). what ever, the thing is even though I copy and paste the same value from firebase debugging it it doesn't want to retrieve the .value from the observeSingleEvent. It just doesn't show up, so I cannot show up the bookmark on the UserProfileController. Is that the Cache that you mention on the description of this video? if so, do you have any video or resource that I can use to get things done? thank youu! 不错不错。
edison1
4 years ago
did you figure this out? I'm trying to figure this out as well
Clint Larenz Nurse
4 years ago
How can I use a label to display the number of likes that post have? Anyway I can grab the Int from the database?
Michael Lustig
4 years ago
Every time you add a like, increment a count. Then, just read the count.
Cinquain
4 years ago
More fire
sharapov0140
3 years ago
There is a bug that I cannot solve. If I press multiple times quickly, it could add multiple like or remove multiple likes from firebase. Has anyone solved that problem?
HELP & SUPPORT