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. The class contains: ( var books: Set ) "I believe the issue could be the Set but, I am not sure."
class Favorites: ObservableObject {
// The actual books the user marked as favorite.
var books: Set<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)
save()
}
func remove(_ book: Book) {
objectWillChange.send()
books.remove(book.id)
save()
}
func save() {
// Write data
}
}
This is where the class is being used:
import SwiftUI
struct BookDetailView: View {
var book: Book
@EnvironmentObject var favorites: Favorites
var body: some View {
ScrollView {
VStack {
Spacer()
ImageAndTitleView(bookInfo: book)
Spacer()
BookDescriptionView(bookInfo: book)
.padding(.top, 45)
Spacer()
VStack(spacing: 20) {
BookCitationView(bookInfo: book)
BookSourceView(bookInfo: book)
}
.padding()
.border(Color.gray, width: 7)
.padding()
.multilineTextAlignment(.center)
}
}
.environmentObject(favorites)
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) {
Button(favorites.contains(book) ? "Remove from Favorites" :
"Add to Favorites") {
if favorites.contains(book) {
favorites.remove(book)
} else {
favorites.add(book)
}
}
}
}
}
}
As of here, the code is working fine. Now comes the part where I want to display the list.
import SwiftUI
struct FavoritesView: View {
var favoritesList: Favorites
var book: Book
var body: some View {
List(favoritesList.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 'Set' to expected argument type 'Binding'
Generic parameter 'Data' could not be inferred
I would appreciate any valuable help on how to resolve this issue.
CodePudding user response:
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
Remarks:
As mentioned in the comments, in
Favorites
you should mark books using@Published
in order forObservableObject
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 anotherView
that needs it.Also in
BookDetailView
, if you need to mutate book from itsView
mark it with@State
. If you need to mutate book from anotherView
, in thatView
mark it with@Binding
.