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
Favoritesyou should mark books using@Publishedin order forObservableObjectto 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: Favoritesor when you are presenting anotherViewthat needs it.Also in
BookDetailView, if you need to mutate book from itsViewmark it with@State. If you need to mutate book from anotherView, in thatViewmark it with@Binding.
