0

I am getting the following error when I try to bind to a nested property of an observed object from Core Data: Cannot convert value of type 'Binding<String?>' to expected argument type 'Binding<String>'

Below is a simplified overview:

Parent.swift

@FetchRequest(
    sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
    animation: .default)
private var items: FetchedResults<Item>

var body: some View {
    NavigationView {
        List {
            ForEach(items) { item in
                Cell(showing: item)
            }
// ...

Cell.swift

struct Cell: View {
    @ObservedObject var item: Item
    
    init(showing: Item) {
        self.item = showing
    }

    var body: some View {
        TextField("Name", text: $item.name) // <-- error shows for this line
// ...

Why does this show an error? How can I create a two-way binding on this textfield with a value stored using Core Data?

Streetlamp
  • 1,537
  • 2
  • 15
  • 27
  • Does this answer your question? [SwiftUI Optional TextField](https://stackoverflow.com/questions/57021722/swiftui-optional-textfield) – lorem ipsum Jul 01 '21 at 22:10
  • @loremipsum I looked at that question but the top answer did not solve my problem – Streetlamp Jul 01 '21 at 22:11
  • The one with bound? That is hands down the best solution for this. It is the cleanest. You can reuse it too. In your code you will use it $item.name.bound and it will work just copy and paste the code anywhere – lorem ipsum Jul 01 '21 at 22:13
  • Shoot, I admit I didn't look past the most upvoted one (by Johnathan), I just tried it and it did end up working actually (good news because it works, but bad because the time expended on the question)! – Streetlamp Jul 01 '21 at 22:21
  • 1
    Don’t be distracted by the most upvoted. Sometimes the lonely comment has the clue you need. – lorem ipsum Jul 01 '21 at 22:24

1 Answers1

0

It is there, in the error - TextField requires String binding, and item.name is String? (optional) binding. You can bypass it by making custom binding. Something like this:

TextField("", text: Binding(get: { item.name }, set: { newValue in item.name = newValue }))
Predrag Samardzic
  • 2,661
  • 15
  • 18
  • Thank you, that worked. I noticed that this does not commit changes back to Core Data, would I need to complete the set portion of the binding to write the changes back or is there a more idiomatic way to do that? – Streetlamp Jul 01 '21 at 22:08
  • 1
    You probably need to save the changes to Core Data, calling managedObjectContext.save() when needed. I would suggest doing the saving on some action, like button press, or textField's onCommit/onSubmit (https://developer.apple.com/documentation/swiftui/textfield/init(_:text:oneditingchanged:oncommit:)-588cl), instead of saving it on every "set". – Predrag Samardzic Jul 01 '21 at 22:13