I'm building a feature that's similar to the "favorites" feature in Apple's Fruta SwiftUI sample app. Users of that app can view a list of smoothies and mark any smoothie as "favorite". The list of favorites is stored and viewable on a separate screen.
The sample view below does two important things:
- It checks if the smoothie is already in the list of favorites with
isFavorite, so that this can be reflected by the button image. - It adds/removes that smoothie to/from the favorites list with
toggleFavorite.
Model:
struct Smoothie: Identifiable, Codable {
var id: String
var title: String
var description: String
var hasFreeRecipe = false
}
class FrutaModel: ObservableObject {
@Published private(set) var favoriteSmoothieIDs = Set<Smoothie.ID>()
func toggleFavorite(smoothie: Smoothie) {
if favoriteSmoothieIDs.contains(smoothie.id) {
favoriteSmoothieIDs.remove(smoothie.id)
} else {
favoriteSmoothieIDs.insert(smoothie.id)
}
}
func isFavorite(smoothie: Smoothie) -> Bool {
favoriteSmoothieIDs.contains(smoothie.id)
}
}
Button view that's used to (un)favorite a smoothie:
struct SmoothieFavoriteButton: View {
@EnvironmentObject private var model: FrutaModel
var smoothie: Smoothie?
var isFavorite: Bool {
guard let smoothie = smoothie else { return false }
return model.favoriteSmoothieIDs.contains(smoothie.id)
}
var body: some View {
Button(action: toggleFavorite) {
Label("Favorite", systemImage: isFavorite ? "heart.fill" : "heart")
}
}
func toggleFavorite() {
guard let smoothie = smoothie else { return }
model.toggleFavorite(smoothie: smoothie)
}
}
My problem: How can I achieve a similar result when using a Toggle instead of a Button? A Toggle requires a binding as a property, but since the isFavorite value for a particular smoothie comes from a Set (favoriteSmoothieIDs) that contains the full favorites list, I can't simply provide a binding for the Toggle.
]