SwiftUIの@State, @Binding, @StateObject, @ObservedObject, @EnvironmentObjectの使い分け
なんとなくSwiftUIでは@Stateと@Bindingを使っていて、@StateObjectを使う必要あるのかなと思っていましたが、Hacking with Swiftというサイトの解説を読んで納得したので自分用のメモ。
@StateObject、@ObservedObject、@EnvironmentObject
・ObjectがついたProperty WrapperはObservable Objectプロトコルに準拠したclassのインスタンスに対してだけ使用できる。
class TestClass: ObservableObject {
@Published var testString: String = "test"
}
・始めに宣言する親Viewでは@StateObjectを使い、渡された子Viewでは@ObservedObjectを使う(@Bindingと同じイメージ)。
struct ParentView: View {
@StateObject var testClassInstance = TestClass()
var body: some View {
ChildView(testClassInstance: testClassInstance)
}
}
struct ChildView: View {
@ObservedObject var testClassInstance: TestClass
var body: some View {
Text(testClassInstance.testString)
}
}
もしくは子Viewの引数に直接Observable Objectのイニシャライザを渡す。子View視点では上の場合と違いはない。
struct ParentView: View {
var body: some View {
ChildView(testClassInstance: TestClass())
}
}
struct ChildView: View {
@ObservedObject var testClassInstance: TestClass
var body: some View {
Text(testClassInstance.testString)
}
}
親Viewで@StateObjectの代わりに@ObservedObjectを使ってしまってもアプリは動くが、謎のエラーの原因になるらしいので非推奨。
・.environmentObjectを使ってObservable Objectに適合したクラスのインスタンスを登録するとそれ以降のViewではどこでも@EnvironmentObjectを使ってそのインスタンスにアクセスできるようになる。
struct ParentView: View {
var body: some View {
ChildView().environmentObject(TestClass())
}
}
struct ChildView: View {var
body: some View {
GrandsonView()
}
}
struct GrandsonView: View {
@EnvironmentObject var testClassInstance: TestClass
var body: some View {
Text(testClassInstance.testString)
}
}
@Stateと@StateObjectの使い分け
・普通のプロパティ→基本的に@State
・classを使いたい場合→@StateObject
・EnvironmentObjectを使いたい場合→@StateObject
私は上記の考え方で使い分けることにしました。
いろんなViewで使うプロパティはいちいち引数に書くのが大変なのでEnvironmentObjectを使います。ただし、ObservableObjectに適合したclassを自分で作る必要があります。
@Stateではclassを監視できないので@StateObjectを使う必要がありますが、ただたくさんのプロパティをまとめたいだけならstructにしてしまう方法もあります。