[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つの画面に対するロジックが分散されてしまい、メンテナンス性が下がる可能性があります。ですので理想としては、
View
に対してViewModel/StateObject/ObservedObject
は1つ
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