こんにちは!むらおです!
SwiftUIで画面分割をした際、親ビューでは@StateObject
を使用するべきだと思いますが、子ビューではどうなのかが気になったので、今回はむらお調査兵団による調査結果をご紹介します
@.StateObjectと@.ObservedObjectの違いについて
細かい説明は他の方の記事を読んでいただきたいのですが、1番の違いはライフサイクルだと思います。
@.StateObject:Viewが表示されてから非表示になるまで
@.ObservedObject:親ビューのbodyが更新されるたび
参考
【SwiftUI】@StateObjectと@ObservedObjectの違いと使い分け
調査報告
コード
以下のようなコードで調査しました。
よくある構成だと思います。
ParentViewのbody内が大きくなったので、ChildViewにstructとして切り出した場面を想定しています。
View
// 親ビュー
struct ParentView<ViewModel: ParentViewModel>: View {
@StateObject var vm: ViewModel
var body: some View {
ChildView(vm: vm)
Text(vm.text)
.border(Color.black)
TextField("placeholder", text: $vm.text)
}
}
// 関係ないビュー
struct ChildView<ViewModel: ParentViewModel>: View {
// ここをいじって調査する
+ @StateObject var vm: ViewModel
var body: some View {
let _ = Self._printChanges()
Text("子View")
}
}
ViewModel
protocol ParentViewModel: ObservableObject {
var text: String { get set }
}
class ParentViewModelImpl: ParentViewModel {
@Published var text: String = ""
}
調査方法
ParentView
のTextField
に文字を入力することで、ChildView
の更新有無を調査しようと思います。
SwiftUI3.0で、Self._printChanges()
というプライベートメソッドが追加されました。これを使用することで、Viewの更新トリガーを調べることができます。
_hoge changed
状態の変化によりViewが再レンダリングされた場合、トリガーが出力される
@.self
View自信が更新されたときに出力される
@.identity
ViewのIDが変わったときに出力される
結果
ChildView
が@StateObject
を使用した場合
TextField
に文字を入力するたびに、
ChildView<ParentViewModelImpl>: @self, _vm changed.
と出力されています。
つまり、ChildView
のView
自信の更新が、vm
の変更をトリガーに行われていることが分かります
ChildView
が@ObservedObject
を使用した場合
TextField
に文字を入力するたびに、
ChildView<ParentViewModelImpl>: _vm changed.
と出力されています。
つまり、vm
に変更があっただけで、何も更新されていないことが分かりました。
結論
子ビューで@StateObject
を使用した場合、親ビューのvm
の状態が変わると、子ビュー本体が更新されるようです。
ここの理解が難しかったので、ChatGPTに聞きました。
ChatGPTの回答
一方、子ビューで@ObservedObject
を使用した場合、親ビューのvm
の変更だけが子ビューに通知され、該当箇所(今回はなし)があれば差分更新されるようです。
親ビューでは、@StateObject
を使用し、子ビューでは、@ObservedObject
を使うべきなのかなと思いました。