how to notify swiftUI View when source of truth has changed

I have a somewhat obscure source of truth related to a like/favourite button. When a user taps the button, the model object is added to the favourites list. When they tap again, the model object is removed from the list.
The actual button is wired to a custom binding, however it is not getting re-drawn when I add/remove items from the favourites list.
What do I need to do to let the button know it needs to redraw?

thanks

Code Block
struct Model: Equatable {
    var name: String
    var isFavourite: Bool {
        Favourites.shared.includes(model: self)}
}
class Favourites {
    static let shared = Favourites()
    var favList: [Model] = []
    func add(model: Model) {
        favList.append(model)
    }
    func remove(model: Model) {
        if let index = favList.firstIndex(where: {$0 == model}) {
            favList.remove(at: index)
        }
    }
    func includes(model: Model) -> Bool {
        return favList.firstIndex(where: {$0 == model}) != nil
    }
}
struct ContentView: View {
    var model = Model(name: "Andrew")
    var favouriteBinding: Binding<Bool> {
        Binding<Bool>(
            get: {
                return self.model.isFavourite
        },
            set: {
                if $0 {
                    Favourites.shared.add(model: self.model)
                } else {
                    Favourites.shared.remove(model: self.model)
                }
        })
    }
    var body: some View {
        FavouriteView(isFavourite: favouriteBinding)
    }
}
struct FavouriteView: View {
    @Binding var isFavourite: Bool
    var body: some View {
        Button(action: {
            self.isFavourite.toggle()
        }) {
            Image(systemName: imageName())
                .foregroundColor(Color.yellow)
                .font(Font.system(size: 40))
        }
    }
    func imageName() -> String {
        return isFavourite ? "star.fill" : "star"
    }
}

Answered by OOPer in 641174022

What do I need to do to let the button know it needs to redraw?

  • Make the class conform to ObservableObject

  • Make the property of the class marked as @Published

  • Use the class as @ObservedObject (or @StateObject or @EnvironmentObject)

Updating an @Binding does not directly trigger UI updates.

Something like this:
Code Block
class Favourites: ObservableObject { //<-
static let shared = Favourites()
@Published var favList: [Model] = [] //<-
//...
}


Code Block
struct ContentView: View {
@StateObject var favourites = Favourites.shared //<-
var model = Model(name: "Andrew")
var favouriteBinding: Binding<Bool> {
Binding<Bool>(
get: {
return self.model.isFavourite
},
set: {
if $0 {
favourites.add(model: self.model) //<-
} else {
favourites.remove(model: self.model) //<-
}
})
}
//...
}


Accepted Answer

What do I need to do to let the button know it needs to redraw?

  • Make the class conform to ObservableObject

  • Make the property of the class marked as @Published

  • Use the class as @ObservedObject (or @StateObject or @EnvironmentObject)

Updating an @Binding does not directly trigger UI updates.

Something like this:
Code Block
class Favourites: ObservableObject { //<-
static let shared = Favourites()
@Published var favList: [Model] = [] //<-
//...
}


Code Block
struct ContentView: View {
@StateObject var favourites = Favourites.shared //<-
var model = Model(name: "Andrew")
var favouriteBinding: Binding<Bool> {
Binding<Bool>(
get: {
return self.model.isFavourite
},
set: {
if $0 {
favourites.add(model: self.model) //<-
} else {
favourites.remove(model: self.model) //<-
}
})
}
//...
}


Thanks OOPer. (and thanks for keeping my Canadian spelling of favourite in your sample code :-)
how to notify swiftUI View when source of truth has changed
 
 
Q