Home > OS >  SwiftUI - Display the views marked as favorites in a list
SwiftUI - Display the views marked as favorites in a list

Time:07-31

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:

  1. Cannot convert value of type 'Set' to expected argument type 'Binding'

  2. 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:

  1. 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]
    
  2. 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.
    
  3. 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.

  4. 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.

  • Related