Search code examples
core-dataswiftuicombine

SwiftUI Bind to core data object inside foreach


I am not able to bind the bookName from my Core Data object to a TextField inside a ForEach loop. How can I get this binding to work? I want the bookName value to be saved to Core Data when it changes.

I am receiving an error that says: Cannot find $book in scope.

extension Book: Identifiable {
    @nonobjc public class func fetchRequest() -> NSFetchRequest<Book> {
        return NSFetchRequest<Book>(entityName: "Book")
    }
    
    @NSManaged public var id: UUID?
    @NSManaged public var bookName: String?
    
    var wrappedBookName: String {
        bookName ?? ""
    }
}

struct BookListView: View {
    @FetchRequest(entity: Book.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Book.rankNumber, ascending: false)]) var books: FetchedResults<Book>
    
    var body: some View {
        ForEach(books) { book in
            TextField("Book Name", text: $book.bookName)  //Error: cannot find $book in scope
        }
    }
}


Solution

  • I love CoreData with SwiftUI. It works just so simple. Just create a new edit view with ObservedObject of Book.

    struct EditView : View {
        @ObservedObject var book: Book
        
        init(book: Book) {
            self.book = book
        }
        
        var body : some View {
            TextField("Name", text: $book.bookName)
        }
    }
    

    And that's it. Then you can send $book.bookName as Binding to the String.

    However, make sure! you have declared bookName as non-optional value in CoreData. TextField requires a Binding<String>, NOT a Binding<String?>

    Use that EditView inside your ForEach and you are ready to go:

    ForEach(books) { book in
        EditView(book: book)