0

In my application, I am trying to prevent the user from liking a post twice. my current code is:

@State private var isLiked = false
HStack {
                Button(action: {if isLiked == false{
                    postData.addLike(id: post.id)
                    isLiked = true
                } else{
                    postData.unLike(id: post.id)
                    isLiked = false
                }}, label: {
                    Image(systemName: isLiked ? "heart.fill" : "heart")
                        .resizable()
                        .aspectRatio(contentMode: .fill)
                        .frame(width: 20)
                        .foregroundColor(isLiked ? .red : .gray)

Currently, when the like button is first pressed, it turns red and "addLike" is performed. However, if I close the app and reopen it, the button is no longer red. I would like the button to stay red, no matter how many times the app is closed/reopened, until the user presses it to unlike it.

I've read about using UserDefaults, but those get cleared when the app is uninstalled. I have my app linked to Google Firebase, so I was thinking that when the app is liked, add the users ID to a column called "likedBy"? Then when the app is opened, have the app scan for the users ID to see if that post was already liked by them. I have no clue how to implement this.

Sorry if this is a stupid question, I am making this app for fun and to learn.

EDIT I've been able to partly solve this thanks to the answer I've marked below. I've set up an array in my backend (postData):

self.ref.collection("Posts").document(id).updateData([
                "likedBy": FieldValue.arrayUnion([self.uid])
self.ref.collection("Posts").document(id).updateData([
                "likedBy": FieldValue.arrayRemove([self.uid])

This code sets the user's UID into the likedBy array. Now, I need to figure out how to read this data, and then see if the user's UID is equal to a UID found on the array.

My new question: SwiftUI Firebase - Reading Array Data to see if string is equal to an array string

3
  • Are you using Firestore or the Realtime Database? Neither really has a "column" per say, but it sounds like you're on the right track with storing the user ID when a user likes an item. You'd then have to read that data on app/view launch and update it whenever the user "likes" or "unlikes" the item. Commented Jun 28, 2021 at 5:38
  • 1
    Not specific to SwiftUI part but are these answering your question about implementing a 'like' functionality in Firestore? stackoverflow.com/questions/60441035/… or stackoverflow.com/questions/48300034/… Commented Jun 28, 2021 at 7:39
  • I'm using Firestore. Not sure why I said "column". Commented Jun 28, 2021 at 16:26

1 Answer 1

1

You have two ways to handle this, both ways require you to think about all possible cases. Option #1, you handle it through Firebase alone. Option #2, you handle it locally and push to Firebase. You already mentioned an issue with Option #2 where uninstalling will cause it to lose data. The obvious, best solution is Option #1, where we let Firebase handle it.

Implementation of Option #1

Let's think about this issue logically, a user likes an item, firebase is updated with a "Reference" to that "Post ID" (I'm assuming). If the app is closed, and re-opened, there is no data indicating that a post is liked.

To fix that, we need to add a check to grab that data that has been saved to firebase. If the data exists, then set it to "Liked" if it doesn't set it to "Default"

In your firebase you might need a document structure like this.

user/profiles/<someProfileId>/likedPosts : [ someID, someID2, someID3 ] 

Each time the app launches, firebase checks this particular document and grabs the related dictionary, at run-time. The purpose for this is #1, it's simple data to grab, #2 it prevents unnecessary Firebase queries. Then, you'll need to store that in some object. I recommend a singleton as you'll be needing that data all over the place. It might look something like this.

class FirebaseDataManager {
     static var shared = FirebaseDataManager()
     var likedPosts: [String] //String or Int ID's to the posts themselves.      

     init() {
          //Fetch our latest liked posts
          super.init()
          likedPosts = fetchRecentLikedPosts()
     }
  
     func fetchRecentLikedPosts() -> [String] {
          //Do whatever logic you need to here. 
          return [String]()
     }

     func addLikedPost(postID: String) {
          //Add to the likedPosts [String] & Firebase
     }
}

Doing it this way will allow you to have a "Source of truth" being the Firebase, which eliminates any problems you might have with de-sync data or with constant queries to the firebase. To boot you can access it from anywhere. Further diving into it, each time a post is shown, check the ID against the FirebaseDataManager.shared.likedPosts array and if there is a match, you know it's liked, set accordingly.

Usage Example

struct someView: View {
  
    let firManager = FirebaseDataManager.shared

    var body: some View { 
        //You should handle the case when there is nothing. 
        //This is merely an example. 
        Text("\(firManager.likedPosts.first())"
    }

}

Documentation: https://firebase.google.com/docs/firestore/query-data/get-data

I encourage you to checkout firebase documentation on some of the concepts I'm mentioning here. Their documentation will help you tremendously. Let me know if you have any issues and I'll try my best to help you out.

Sign up to request clarification or add additional context in comments.

4 Comments

As you said, I think the best option is Option #1. When a user likes an item, "likes" is incremented by one. Likes in Firebase is located at Posts/(post id)/likes. Likes is just a number that gets incremented. To my understanding, you're saying to create a new section in my "Users/(user)" collection (I called it users not Profiles) and have every post ID that user has liked in that section? I was thinking about doing that, however I have no clue how to add more IDs to one section. I know how to add [someID] to the string, but I don't know how to add the other [someID2, 3] to the same string.
Also, with init() { likedPosts = fetchRecentLikedPosts() } I'm getting the error "self used in method call fetchRecentLikedPosts before stored all properties are initialized"
forgot that you have to call super.init()
use Firebase's .UpdateValue([String:Any]) to create a dictionary on the document itself. Don't use a string. So you might have a value like this. ["likedPosts" : [ "post1", "post2" ] Firebase will automatically determine the type of Any and put it in appropriately.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.