[SwiftUI]StateObjectの挙動から戦略を考える
@StateObjectとは
iOS14から使用でき、ObservableObjectを扱えます。ObservedObjectと似ていますが、挙動が違います。
ObservedObject
親Viewが再描画されたら、その度に更新されます。実装方法によっては初期化されてしまいます。
struct ObservedChildView: View {
@ObservedObject var counter = Counter()
//親Viewが更新されたら、このCounterは初期化される
var body: some View {
HStack {
Text("[ObservedObject]Increment")
Button.init(action: { counter.increment() }, label: {
Image(systemName: "plus")
})
Text("\(counter.count)")
}
}
}
StateObject
親Viewが再描画されても、値は保持されます。
struct StateChildView: View {
@StateObject var counter = Counter()
//親Viewが更新されても、このCounterは保持されたままです
var body: some View {
HStack {
Text("[StateObject]Increment")
Button.init(action: { counter.increment() }, label: {
Image(systemName: "plus")
})
Text("\(counter.count)")
}
}
}
ObservedObject/StateObjectは画面に1つであるべきか
@ObservedObjectや@StateObjectを設定できるのは、ObservableObject protocolを継承しているクラスのみです。MVVMアーキテクチャを考える場合、ObservableObjectはViewModelに当たり、ロジックが中心となっています。
一般的なMVVMを考えるのであれば、1つの画面にはViewModelが1つ、つまりObservableObjectもしくはStateObjectは1つということになります。
iOS14以前は、子ViewにObservedObjectを設定していても初期化されてしまう場合があるので、1つの画面にViewModel/ObservableObjectは1つであることがほとんどでした。しかし、StateObjectが出たことによりViewModel/StateObjectが複数あっても挙動には問題がなくなりました。
もう一度MVVMアーキテクチャを考えてみると、ViewModel/StateObjectが複数ある場合、1つの画面に対するロジックが分散されてしまい、メンテナンス性が下がる可能性があります。ですので理想としては、
1つのViewに対してViewModel/StateObject/ObservedObjectは1つ
が良いと考えられます。
もし、ViewModelで取得した値を子Viewで使いたい場合はBindingを利用することを推奨します。
StateObjectが画面に2つ以上ある場合を考える
StateObjectがViewの中に複数ある場合を考えます。
- 親View
- 子View.1(StateObject.1)
- 子View.2(StateObject.2)
このような構成の場合、前述のViewとViewModelが1対1であることからは外れてしまいますが、Viewの部分更新による描画コストの節約が可能と考えられます。
具体的なレイアウトを考えると
- マイページ
- アカウント情報
- 最近投稿したコンテンツ
など、1つの画面で2つ程度の大きなカテゴリの情報を表示する場合が考えられます。上記の場合、アカウント情報はあまり変更ありませんが、最近投稿したコンテンツは頻繁に更新があると考えられます。
全体の更新(再描画)は避けることで、描画パフォーマンスが上がると考えられます。
とはいえ、1つのViewにViewModel/StateObjectが4つかそれ以上もあるとロジックも分散され、メンテナンス性が下がります。複数あるとしても、2〜3個までにしたほうが良いでしょう。
結論
StateObjectの挙動、MVVMアーキテクチャから考えると、
-
ViewModel/StateObjectは基本1つの画面に対して1つ - 子Viewには
Bindingで情報を渡す - 再描画コスト・パフォーマンスが気になる場合は1つの画面に対して
ViewModel/StateObjectは2〜3個程度
参考
Discussion