SwiftUIにおけるObservableObjectの管理
SwiftUIでデータを監視可能にするためにはObservableObjectプロトコルに準拠したオブジェクトを作成します。
class Book: ObservableObject {
@Published var title = "Great Expectations"
}
ObservableObjectに準拠したオブジェクトをSwiftUIから監視対象とする方法は3つ用意されています。
StateObject
StateObjectはViewに対してインスタンスを1つだけ生成して管理する振る舞いをします。そのためアプリケーション全体で使用するようなデータ(モデル)の管理に向いています。
@main
struct BookReader: App {
@StateObject var library = Library()
var body: some Scene {
WindowGroup {
...
}
}
}
ObservedObject
ObservedObjectはStateObjectとは異なりViewのライフサイクルに合わせてインスタンスが管理されます。ObservedObjectを使ってインスタンスを生成するとViewの再生成のタイミングでデータが消えてしまうといった副作用が起こる可能性があります。また、Viewを生成するたびにインスタンスも生成されるのでオーバーヘッドが大きくなることがあります。ObservedObjectではインスタンスの生成は行わずに上位のViewなどから渡されたインスタンスを参照する目的で使用するのが良いです。
struct LibraryView: View {
@StateObject var book = Book()
var body: some View {
BookView(book: book)
}
}
struct BookView: View {
@ObservedObject var book: Book
var body: some View {
BookEditView(book: book)
}
}
struct BookEditView: View {
@ObservedObject var book: Book
// ...
}
EnvironmentObject
EnvironmentObjectは階層が上位のViewからenvironmentObject()で渡されたインスタンスにアクセスできます。ObservedObjectとは異なり直接の親ではないViewからの受け取りが可能になるのでインスタンスのバケツリレーが不要です。アプリ全体で使用するオブジェクトを多数のViewや深い階層の子Viewに渡したい場合に便利です。
@main
struct BookReader: App {
@StateObject var library = Library()
var body: some Scene {
WindowGroup {
LibraryView()
.environmentObject(library)
}
}
}
struct LibraryView: View {
@EnvironmentObject var library: Library
// ...
}
どのように使い分けると良いのか
インスタンスの生成はStateObject
で行い、インスタンスを参照するときにObservedObject
もしくはEnvironmentObject
を使うのが良いと思います。
-
App, Scene, View
でインスタンスを生成するときはStateObject
を使う- Viewのライフサイクルとは独立してオブジェクトを管理できる
-
親View
からインスタンスを受け取るときはObservedObject
を使う- Viewのライフサイクルによってオブジェクトの生成と破棄が行われる
-
environmentObject
によって渡されたときはEnvironmentObject
を使う- アプリのトップレベルで宣言したインスタンスを全てのViewから参照できる
参考資料
Managing Model Data in Your App
Discussion