I'm starting with SwiftUI and following WWDC videos I'm starting with @State
and @Binding
between two views. I got a display right, but don't get how to make back-forth read-write what was not include in WWDC videos.
I have model classes:
class Manufacturer {
let name: String
var models: [Model] = []
init(name: String, models: [Model]) {
self.name = name
self.models = models
}
}
class Model: Identifiable {
var name: String = ""
init(name: String) {
self.name = name
}
}
Then I have a drawing code to display that work as expected:
var body: some View {
VStack {
ForEach(manufacturer.models) { model in
Text(model.name).padding()
}
}.padding()
}
and I see this: Canvas preview picture
But now I want to modify my code to allows editing this models displayed and save it to my model @Binding
so I've change view to:
var body: some View {
VStack {
ForEach(self.$manufacturer.models) { item in
Text(item.name)
}
}.padding()
}
But getting and error in ForEach
line:
Generic parameter 'ID' could not be inferred
What ID parameter? I'm clueless here... I thought Identifiable
acting as identifier here.
My question is then:
I have one view (ContentView) that "holds" my datasource as @State
variable. Then I'm passing this as @Binding
to my ManufacturerView
want to edit this in List
with ForEach
fill but cannot get for each binding working - how can I do that?
First, I'm assuming you have something like:
@ObservedObject var manufacturer: Manufacturer
otherwise you wouldn't have self.$manufacturer
to begin with (which also requires Manufacturer
to conform to ObservableObject
).
self.$manufacturer.models
is a type of Binding<[Model]>
, and as such it's not a RandomAccessCollection
, like self.manufacturer.models
, which is one of the overloads that ForEach.init
accepts.
And if you use ForEach(self.manufacturer.models) { item in ... }
, then item
isn't going to be a binding, which is what you'd need for, say, a TextField
.
A way around that is to iterate over indices, and then bind to $manufacturer.models[index].name
:
ForEach(manufacturer.indices) { index in
TextField("model name", self.$manufacturer.models[index].name)
}
In addition to that, I'd suggest you make Model
(and possibly even Manufacturer
) a value-type, since it appears to be just a storage of data:
struct Model: Identifiable {
var id: UUID = .init()
var name: String = ""
}
This isn't going to help with this problem, but it will eliminate possible issues with values not updating, since SwiftUI wouldn't detect a change.