Sharing state with @EnvironmentObject
Inject an ObservableObject into the environment and read it from the descendant views that need it.
Read the Korean original on Velog ↗English translation

Passing state through several levels can force intermediate views to carry data they never use. An environment object lets a descendant access a shared object without passing it through every initializer.
Passing data between views
@StateObject and @ObservedObject allow views to share an ObservableObject. Explicitly passing that reference works, but intermediate views may need properties solely to forward it to their children.
// Shared data
class EnvironmentViewModel: ObservableObject {
@Published var dataArray: [String] = []
init() {
getData()
}
func getData() {
self.dataArray.append(contentsOf: ["iPhone", "iPad", "iMac"])
}
}
// First view
struct FirstView: View {
// Create data
@StateObject var viewModel: EnvironmentViewModel = EnvironmentViewModel()
var body: some View {
NavigationStack {
List {
ForEach(viewModel.dataArray, id: \.self) { item in
NavigationLink {
SecondView(viewModel: viewModel, selectedItem: item)
} label: {
Text(item)
}
}
}
}
}
}
// Second view
struct SecondView: View {
// Forward data through an intermediate view
@ObservedObject var viewModel: EnvironmentViewModel
let selectedItem: String
var body: some View {
NavigationLink {
ThirdView(viewModel: viewModel)
} label: {
Text(selectedItem)
}
}
}
// Third view
struct ThirdView: View {
// Share the data
@ObservedObject var viewModel: EnvironmentViewModel
var body: some View {
ForEach(viewModel.dataArray, id: \.self) { item in
Text(item)
}
}
}

@EnvironmentObject
A parent can own an object with @StateObject and inject it using .environmentObject(). Descendants read the shared instance with @EnvironmentObject, without requiring every intermediate view to pass it along.
struct FirstView: View {
// Create data
@StateObject var viewModel: EnvironmentViewModel = EnvironmentViewModel()
var body: some View {
NavigationStack {
List {
ForEach(viewModel.dataArray, id: \.self) { item in
NavigationLink {
SecondView(selectedItem: item)
} label: {
Text(item)
}
}
}
}
// Share the object with descendants
.environmentObject(viewModel)
}
}
struct SecondView: View {
// No intermediate forwarding needed
let selectedItem: String
var body: some View {
NavigationLink {
ThirdView()
} label: {
Text(selectedItem)
}
}
}
struct ThirdView: View {
// Share the data
@EnvironmentObject var viewModel: EnvironmentViewModel
var body: some View {
ForEach(viewModel.dataArray, id: \.self) { item in
Text(item)
}
}
}
