@Observable @State @Binding @Bindable 付ける付けない調査隊
毎年仕様が変わるので毎年やってますなw
機械的に付ける付けないをいろんなパターン試して挙動を調査するものです。
意味がない組み合わせがあっても気にしません。
やること
モデルを親Viewで作って子Viewに渡す。
イメージ図

モデル
@Observable
class Book {
var title = String(Int.random(in: 0...100000))
}
@Observable を付ける付けないの2通り試します。
今回は class のみやります。
親View
struct ObservationTest1: View {
@State var book = Book()
var body: some View {
Button {
book = Book()
} label: {
Text("New")
}
Button {
book.title = String(Int.random(in: 0...100000))
} label: {
Text("Change")
}
Text(book.title)
//ObservationTest2(book: book)
//ObservationTest3(book: $book)
ObservationTest4(book: book)
}
book に @State を付ける付けないの2通り試します。
@State を付ける場合は
- Newボタンあり
@State を付けない場合は
- Newボタンなし(変更不可のエラーが出るのでなし)
共通して
- book.titleを表示する
- 子Viewを呼ぶ
子View
var book: Book が定義してあるObservationTest2。
@Binding var book: Book が定義してあるObservationTest3。
@Bindable var book: Book が定義してあるObservationTest4。
ObservationTest2
struct ObservationTest2: View {
var book: Book
var body: some View {
Button {
book.title = String(Int.random(in: 0...100000))
} label: {
Text("Change")
}
Text(book.title)
}
ObservationTest2は
- book.titleを変更するボタンがある
- book.titleを表示する
ObservationTest3
struct ObservationTest3: View {
@Binding var book: Book
var body: some View {
Button {
book = Book()
} label: {
Text("New")
}
Button {
book.title = String(Int.random(in: 0...100000))
} label: {
Text("Change")
}
Text(book.title)
}
ObservationTest3は
- bookを変更するボタンがある
- book.titleを変更するボタンがある
- book.titleを表示する
ObservationTest4
struct ObservationTest4: View {
@Bindable var book: Book
var body: some View {
Button {
book.title = String(Int.random(in: 0...100000))
} label: {
Text("Change")
}
Text(book.title)
}
}
ObservationTest4は
- book.titleを変更するボタンがある
- book.titleを表示する
book への再代入は出来ないので、book の変更はありません。
@Observableなし @Stateなし @Bind〜なし
| 親で titleを 更新すると |
子で titleを 更新すると |
|
|---|---|---|
| 親Viewの Text(book.title)は |
更新なし | 更新なし |
| 子Viewの Text(book.title)は |
更新なし | 更新なし |
@Observableなし @Stateなし @Bindingあり
エラー
渡せない
@Observableなし @Stateなし @Bindableあり
エラー
@Bindable が @Observable を要求
@Observableなし @Stateあり @Bind〜なし
| 親で bookを 新規作成すると |
親で titleを 更新すると |
子で titleを 更新すると |
|
|---|---|---|---|
| 親Viewの Text(book.title)は |
更新 | 更新なし | 更新なし |
| 子Viewの Text(book.title)は |
更新 | 更新なし | 更新なし |
| 親のbody再構築 |
@Observableなし @Stateあり @Bindingあり
| 親で bookを 新規作成すると |
親で titleを 更新すると |
子で bookを 新規作成すると |
子で titleを 更新すると |
|
|---|---|---|---|---|
| 親Viewの Text(book.title)は |
更新 | 更新なし | 更新 | 更新なし |
| 子Viewの Text(book.title)は |
更新 | 更新なし | 更新 | 更新なし |
| 親のbody再構築? | 親のbody再構築? |
@Observableなし @Stateあり @Bindableあり
エラー
@Bindable が @Observable を要求
@Observableあり @Stateなし @Bind〜なし
| 親で titleを 更新すると |
子で titleを 更新すると |
|
|---|---|---|
| 親Viewの Text(book.title)は |
更新 | 更新 |
| 親Viewの Text(book.title)は |
更新 | 更新 |
@Observableあり @Stateなし @Bindingあり
エラー
渡せない
@Observableあり @Stateなし @Bindableあり
| 親で titleを 更新すると |
子で titleを 更新すると |
|
|---|---|---|
| 親Viewの Text(book.title)は |
更新 | 更新 |
| 子Viewの Text(book.title)は |
更新 | 更新 |
@Observableあり @Stateあり @Bind〜なし
| 親で bookを 新規作成すると |
親で titleを 更新すると |
子で titleを 更新すると |
|
|---|---|---|---|
| 親Viewの Text(book.title)は |
更新 | 更新 | 更新 |
| 子Viewの Text(book.title)は |
更新 | 更新 | 更新 |
| 親のbody再構築 |
@Observableあり @Stateあり @Bindingあり
| 親で bookを 新規作成すると |
親で titleを 更新すると |
子で bookを 新規作成すると |
子で titleを 更新すると |
|
|---|---|---|---|---|
| 親Viewの Text(book.title)は |
更新 | 更新 | 更新 | 更新 |
| 子Viewの Text(book.title)は |
更新 | 更新 | 更新 | 更新 |
| 親のbody再構築? | 親のbody再構築? |
@Observableあり @Stateあり @Bindableあり
| 親で bookを 新規作成すると |
親で titleを 更新すると |
子で titleを 更新すると |
|
|---|---|---|---|
| 親Viewの Text(book.title)は |
更新 | 更新 | 更新 |
| 子Viewの Text(book.title)は |
更新 | 更新 | 更新 |
| 親のbody再構築? |
@State @Bindable が必要な理由
@Observable にすると title の変更に反応するので上の調査では @State や @Bindable は要らないように見えるが、@State や @Bindable はさらに下にBindingで渡すときに必要になる。
struct BookEditView: View {
@Bindable var book: Book
@Environment(\.dismiss) private var dismiss
var body: some View {
VStack() {
HStack {
Text("Title")
TextField("Title", text: $book.title) //ここで必要
.textFieldStyle(.roundedBorder)
.onSubmit {
dismiss()
}
}
Button("Close") {
dismiss()
}
.buttonStyle(.borderedProminent)
}
.padding()
}
}
考察
上の調査では
@Observable あり @State あり @Binding ありと
@Observable あり @State あり @Bindable ありは、
インスタンスを新しく作成すること以外は一緒の結果。@Bindable は再代入しようとするとエラーが出る。これはわざとか?
@Binding よりも @Bindable が良い点としては @Observable の付け忘れでエラーが出る点がある。
@Observable なし @State あり @Binding ありはエラーが出ず、title を変更してもViewが更新されない。
@Observable なし @State あり @Bindable ありはエラーになる。@Bindable が @Observable を要求するので。
さいごに
Observationの学習中に書いたもので理解が不十分かもしれません。何か指摘がある場合はよろしくお願いします。
Discussion