やりたいこと
SwiftDataで定義したモデルTodoItem
をForEach内で展開し、Toggleにバインディングとして渡してTodoItem.isDone
とチェックボックスを連動させたかった。
躓いたこと
ForEachでtodo
として展開してToggleにそのまま渡すと、当然ながら
Cannot convert value of type 'Bool' to expected argument type 'Binding<Bool>'
と怒られる。
なので何らかの形でtodo
ないしはisDone
をバンディングにしてあげないといけない。
そこでこちらを参考に、以下のようにしてみたところ。。。
ForEach(todos) { todo in
let bindedTodo = Bindable(todo)
Toggle(isOn: bindedTodo.isDone) { // ①
// ...
}
@Bindable var bt = todo
Toggle(isOn: bt.isDone) { // ②
// ...
}
// ...
}
①はうまくいった。が②は↓のエラーが出る。
Generic parameter 'V' could not be inferred
ちなみに②を①の前に移動させたら↓に変わる。
Cannot reference invalid declaration 'todo'
解決策
②ではToggle
に渡す際にドルマーク$
をつけないといけないらしく、そこが原因だったもよう。
なので正しいコードは次の通り。
ForEach(todos) { todo in
let bindedTodo = Bindable(todo)
Toggle(isOn: bindedTodo.isDone) {
// ...
}
@Bindable var bt = todo
Toggle(isOn: $bt.isDone) { // bt -> $bt
// ...
}
// ...
}
考察
Bindable()
はBindable型のオブジェクトを生成するイニシャライザであり、
create bindings to mutable properties of a data model object that conforms to the Observable protocol.
だそうなので、それ自体がバインディングを有すると。
一方で@Bindable
は付与しただけでバインディングになるわけではなく、バインディングを得るには$
またはprojectedValueを用いる必要があると。
わかったようなわからないような。いや、わからない。
とりあえずバインディングやプロパティラッパーについての理解が乏しい(表面的)ことは明らかなので、一旦それらを深掘りしたい。
以上
参考
https://zenn.dev/holoholo/articles/db774b4eea9d67
https://developer.apple.com/documentation/swiftui/bindable/init(wrappedvalue:)