2

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
        }
    }
}

Kaunteya
  • 3,107
  • 1
  • 35
  • 66
AAV
  • 373
  • 1
  • 5
  • 17

1 Answers1

6

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)
davidev
  • 7,694
  • 5
  • 21
  • 56
  • 1
    Just tried changing a field to non-optional and set the default text to something or empty string (in the property inspector for the Core Data attribute). Still seeing the Binding conversion error. Did a clean build and quit Xcode (13.2.1) too. It's as if the View still thinks the Core Data definitions haven't been updated. Only solution so far is to use this unwrapping technique. https://stackoverflow.com/questions/68543882/cannot-convert-value-of-type-bindingstring-to-expected-argument-type-bindi – obj-D Jan 13 '22 at 00:20
  • another solution is to do this `TextField("Name", text: Binding($book.bookName)!)` – Louis Law Mar 15 '22 at 03:13