Home > Back-end >  Binding in SwiftUI ForEach from Parent to Child
Binding in SwiftUI ForEach from Parent to Child

Time:10-06

I have a Subtask defined like this:

struct Subtask: Identifiable{
  var id = UUID().uuidString
  var text = ""
}

I populate an array of them in an ObservableObject like this:

//View Model
class SubtaskModel: ObservableObject {
  static let shared = SubtaskModel()

  @Published var subtasks = [Subtask]()
  
  init(){
    //Code that creates the subtasks array
  }
}

Then I render them starting in my parent view like this:

//Parent View
struct ParentView: View {
  @StateObject var model = SubtaskModel.shared
  ...
  
  ForEach($model.subtasks) { subtask in
    ChildView(subtask: $subtask)
  }
}

With the intent of being able to modify each individual Subtask in the child view:

//Child View
struct ChildView: View{
  @Binding var subtask: Subtask
  
  ...
  TextField("Text...", text: $subtask.text, onCommit:{
    print("Save subtask!")
  })
}

I'm getting an error in the ParentView on this line:

ChildView(subtask: $subtask)

...that says:

Cannot find '$subtask' in scope

So it seems the ForEach is not returning a binding for each of the subtasks returned by my view model. I have tried other variations of the ForEach but still get the error:

ForEach(model.subtasks) { subtask in
ForEach($model.subtasks, id: \.self) { subtask in
ForEach($model.subtasks, id: \.id) { subtask in

How can I bind a child Subtask from a parent to a child view?

CodePudding user response:

You missed the $ before subtask.

If you don't write $subtask, then subtask is now a Binding<Subtask>. This would mean you can still do subtask.wrappedValue to use subtask normally and subtask to use the binding, but that's not as neat. With the $, $subtask is a Binding<Subtask> and subtask is a Subtask.

Change:

ForEach($model.subtasks) { subtask in
    ChildView(subtask: $subtask)
}

To:

ForEach($model.subtasks) { $subtask in
    ChildView(subtask: $subtask)
}

Or alternatively (and more confusingly, I don't recommend):

ForEach($model.subtasks) { subtask in
    ChildView(subtask: subtask)
}
  • Related