@EnvironmentObjectとはどう使う?
📕Overview
A property wrapper type for an observable object that a parent or ancestor view supplies.
親または祖先ビューが提供するobservableオブジェクトのプロパティラッパータイプ。
どのような目的で使うのか、どんな意味なのかというと、違うページで状態を共有することができます。
こちらの動画が参考になりました。
🧷summary
状態をグローバルに扱うことができるDataExampleというクラスを作成します。ObservableObjectというプロトコルに準拠しています。これは呼び出すことで、値を保持している stored propertyを使い、テキストとカウンターの数値をどこのページでも渡すことができます。
もし@Stateだったら、ページを作っているstructの中でしか状態を扱えないですね。
VStackに、.で繋げているenvironmentObjectは、Data classであるDataExampleを紐つけています。
TextBlockViewとSampleViewは別Viewでこちらでは、EnvironmentObjectを使って、DataExampleの状態を渡しています。
import SwiftUI
@MainActor class DataExample : ObservableObject {
@Published var text = "Counter"
@Published var counter = 0
}
struct EnvironmentObjectView: View {
@StateObject var data = DataExample()
var body: some View {
VStack {
TextBlockView()
SampleView()
}
.environmentObject(data)
}
}
#Preview {
EnvironmentObjectView()
}
TextBlockViewは、図形の中に、EnvironmentObjectを使って、DataExampleの状態を渡しています。
図形をTapすると、onTapGestureの機能で、counterプロパティの状態が更新されて数字が増えて行きます。他のページでも状態が共有されているので、他のSampleViewでも値は増えていきます。
import SwiftUI
struct TextBlockView: View {
@EnvironmentObject var data : DataExample
var body: some View {
ZStack {
Rectangle()
.frame(width: 200, height: 60)
.foregroundColor(.blue)
.cornerRadius(9)
Text("\(data.text) : \(data.counter)")
.foregroundColor(.white)
}
.onTapGesture {
data.counter += 1
}
}
}
#Preview {
TextBlockView()
}
SampleViewには、ボタンがないので値は増やせないですね。でもTextBlockViewで状態が変更されると、プロパティの値は変化します。状態を監視しているからできるのでしょうね。いや状態が共有されているのか...
import SwiftUI
struct SampleView: View {
@EnvironmentObject var data : DataExample
var body: some View {
Text("\(data.text) : \(data.counter)")
}
}
#Preview {
SampleView()
.environmentObject(DataExample())
}
図形をTapするとこんな感じで別のViewのカウンターも増えていきます。

🧑🎓thoughts
今回は、@EnvironmentObjectを使って違うページで状態を扱うことをやってみました。データにアクセスするが正しいのかもしれない。最近よく使うことが多かったので、SwiftUIの知識のアウトプットのために調べてロジックを書いてみました。
わかりやすくて参考になる日本語の記事もありました。
Discussion