Just in case, the Book struct is below.
struct Book: Identifiable {
var id = UUID().uuidString
var title: String
var description: String
var imageURL: URL
var sourceOfDesc: String
var descSourceCitation: String
}
My goal is to display a list of BookDetailView marked as favorites. A class to add and remove books from the favorites has been created.
class Favorites: ObservableObject {
// The actual books the user marked as favorite.
@Published var books: [String]
// The key to be used to read/write in the UserDefaults
private let saveKey = "Favorites"
init() {
// Load saved data
books = []
}
// Returns true if the set contains this book
func contains(_ book: Book) -> Bool {
books.contains(book.id)
}
func add(_ book: Book) {
objectWillChange.send()
books.insert(book.id, at: 0)
save()
}
func remove(_ book: Book) {
objectWillChange.send()
books.removeAll { $0 == book.id }
save()
}
func save() {
// Write data
}
}
Updated FavoritesView below.
struct FavoritesView: View {
@ObservedObject var favoriteList: Favorites
var book: Book
var body: some View {
List(favoriteList.books) { book in
NavigationLink {
WorksListTemplateView(books: book)
} label: {
Text(book.title)
}
}
}
}
I get multiple error messages on FavoritesView, the ones that jump out to me are these 2:
Cannot convert value of type '[String]' to expected argument type 'Binding'
Generic parameter 'Data' could not be inferred
List
is excepting a RandomAccessCollection
, Set
doesn't not conform to it. So you should convert your set to an array: Array(favoritesList.books)
.
However, since String
does not conform to Identifiable
, you also need to add an id to the list:
List(Array(favoritesList.books), id: \.self) { book in
As mentioned in the comments, in Favorites
you should mark books using @Published
in order for ObservableObject
to take effect:
@Published var books: Set<String> //or an array [String]
In FavoritesView
, favoriteList should be:
@ObservedObject var favoriteList: Favorites
//ObservedObject & not StateObject because the object is passed
//down from another View & not directly initialized in the View.
In BookDetailView
, you don't need .environmentObject(favorites)
. You inject it when you first Initialize it, i.e where you don't have @EnvironmentObject var favorites: Favorites
or when you are presenting another View
that needs it.
Also in BookDetailView
, if you need to mutate book from its View
mark it with @State
. If you need to mutate book from another View
, in that View
mark it with @Binding
.