0
struct MakeVideo: View {
    
    @EnvironmentObject var modelData: ModelData

    @State private var chosenFriends: [FriendModel] = []
    
    mutating func addFriend(_friend: FriendModel) -> Void {
        chosenFriends.append(_friend)
        
    }
    
    var body: some View {
        VStack {
            ForEach(modelData.friends) { friend in
                HStack {
                    ProfilePic(picture: friend.profilepic!)
                    Text("@"+friend.username!)
                    
                    //TODO: This is updating the val, it just isn't being shown here
                    
                    Button("Add", action: friend.toggleChosen)
                    
                    if friend.isChosen {
                        Image(systemName: "star.fill")
                            .foregroundColor(.yellow)
                    } else {
                        Image(systemName: "star")
                    }
                    
                }
            }
        }
    }
}

struct MakeVideo_Previews: PreviewProvider {
    static var previews: some View {
        MakeVideo()
            .environmentObject(ModelData())
    }
}

I am trying to dynamically update this so that when ai click the Button, it'll make the star be filled instead of empty. In the debugger I see the class value being changed however the change does not appear in the view. I also made the var in the class @Published, which I thought would allow the view to change with the value

Here is my code for the classes and ModelData

class FriendModel: Identifiable, ObservableObject {
    
    init(id: Int, username: String, profilepic: String) {
        self.id = id
        self.username = username
        self.profilepic = profilepic
    }
    
    var id: Int?
    var username: String?
    var profilepic: String?
    @Published var isChosen = false
    //var profilepic: UIImage
    
    func toggleChosen() {
        print(self.isChosen)
        self.isChosen = !self.isChosen
        print(self.isChosen)
    }
}
var allvideos: [VideoModel] = [VideoModel(id: 1, name: "Beach Trip", length: 25, url: "mona"), VideoModel(id: 2, name: "Dogs", length: 10, url:"dog"), VideoModel(id: 3, name: "Concerts", length: 42, url: "hogrider")]

var allfriends: [FriendModel] = [FriendModel(id: 1, username: "bpaul18", profilepic: "profilepic"), FriendModel(id: 2, username: "kmill", profilepic: "profilepic"), FriendModel(id: 3, username: "dudeitsdom", profilepic: "profilepic")]

final class ModelData: ObservableObject {
    @Published var videos: [VideoModel] = allvideos
    @Published var friends: [FriendModel] = allfriends
}
2
  • 1
    Show your FriendModel and ModelData related code. Commented Aug 10, 2022 at 5:47
  • The problem is that you update the local friend var not the element in the array : try something like friends[friends.firstIndex(where: { $0 == friend}] = friend . The best is to add a method in the model to do this. There are some example on internet/SO to learn how to do this. Commented Aug 10, 2022 at 6:49

3 Answers 3

1

You don't say that you get a compiler error on the following line:

Button("Add", action: friend.toggleChosen)

Therefore I deduce that FriendModel is a class, not a struct. If FriendModel were a struct and toggleChosen were a mutating method, then you would get an error: “Cannot use mutating member on immutable value”.

Even if FriendModel conforms to ObservableObject, the problem is that ObservableObjects do not automatically compose. A change to an @Published property of a FriendModel will not be noticed by a containing ModelData, which means (in this case) that SwiftUI will not notice that friend.isChosen was modified.

I suggest making FriendModel into a struct.

I also recommend using Point-Free's Composable Architecture package (or something similar) as your app architecture, because it provides a comprehensive solution to problems like this.

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

Comments

0

try using this:

 ForEach($modelData.friends) { $friend in // <-- here $

Comments

0

Body is only called for states that are used. Since chosenFriends is not used in body, it is not called when it is changed in a button action.

To fix, write a func isFriend and lookup the friend ID in the chosenFriends array and use the result of that in body to show the star. Since chosenFriends is now used, body will be called. Also change the button to call the addFriend func which would be better named as chooseFriend by the way.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.