오늘은 EnvironmentObject에 관해 정리해 보겠습니다.
먼저 정의를 보도록 하겠습니다.
@MainActor @frozen @propertyWrapper @preconcurrency
struct EnvironmentObject<ObjectType> where ObjectType : ObservableObject
프로퍼티 래퍼이고, ObservableObject 프로토콜을 준수네요.
공식문서에서는 부모 또는 상위 뷰가 제공하는 관찰 가능 객체에 대한 속성 래퍼 유형이라고 되어 있습니다.
즉, EnvironmentObject
는 상위 뷰에서 하위 뷰로 관찰 가능한 객체를 전달하여 전역적으로 데이터를 공유하는 형태로, 하위 뷰는 전달받은 객체의 데이터에 접근 및 수정을 할 수 있도록 하는 프로퍼티입니다.
EnvironmentObject를 사용하기 위해서는 아래의 단계를 따라야 합니다.
ObservableObject
프로토콜을 준수한 클래스를 정의하고, 공유할 데이터는 @Published
프로퍼티 래퍼를 사용하여 선언합니다..environmentObject()
메소드를 통해 주입됩니다.@EnvironmentObject
프로퍼티 래퍼를 사용하여 주입된 객체에 접근할 수 있습니다.import SwiftUI
// 1. ObservableObject 프로토콜을 채택한 클래스 정의
class SharedData: ObservableObject {
@Published var age: Int = 24
}
// 2. 환경 객체를 사용하는 뷰
struct ContentView: View {
@EnvironmentObject var data: SharedData
var body: some View {
Button {
data.age += 1
} label: {
Text("age: \(data.age)")
}
}
}
// 3. 앱의 최상위 뷰에서 환경 객체 제공
@main
struct MyApp: App {
@StateObject private var sharedData = SharedData()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(sharedData)
}
}
}
SharedData라는 객체를 StateObject를 통해 생성하고, 이를 environmentObject를 통해 ContentView라는 하위 뷰에 주입하여 ContentView에서 ShareData의 age를 변경하면, 이를 관찰하고 있는 모든 뷰는 동시에 업데이트를 진행합니다.
이처럼 EnvironmentObject는 복잡하지 않고 편하게 다른 뷰들과 데이터를 공유할 수 있는 프로퍼티입니다.
EnvironmentObject는 사용하기 전에 반드시 상위 뷰에서 제공되어야 합니다. 만약 이를 제공하지 않고 하위 뷰에서 접근하려고 하면 런타임 오류가 발생하게 됩니다.
아래 코드를 보면서 더 자세히 설명해 보도록 하겠습니다.
@main
struct MyApp: App {
@StateObject private var sharedData = SharedData()
var body: some Scene {
WindowGroup {
NavigationStack {
// ContentView는 모든 하위 뷰를 포함한다고 가정
ContentView()
.environmentObject(sharedData)
}
}
}
}
위 코드는 ContentView
에서 NavigationLink
를 사용해 다른 페이지로 이동하여 SharedData
에 접근하면 런타임 오류가 발생합니다.
이런 일이 발생하는 이유는 ContentView가 최상위 뷰가 아니고, NavigationStack
이 최상위 뷰이기 때문입니다.
NavigationStack 역시 뷰인데, NavigationLink를 통해 진입한 뷰는 NavigationView의 하위 뷰이기 때문에 오류가 발생한 것입니다.
따라서 이를 위해서는 NavigationStack
에도 environmentObject
메서드를 통해 객체를 주입해 줘야 합니다.
@main
struct MyApp: App {
@StateObject private var sharedData = SharedData()
var body: some Scene {
WindowGroup {
NavigationStack {
// ContentView는 모든 하위 뷰를 포함한다고 가정
ContentView()
.environmentObject(sharedData)
}
.environmentObject(sharedData)
}
.environmentObject(sharedData)
}
}
가장 깔끔한 방법은 앱의 진입점인 WindowGroup에 주입하는 것입니다.
EnvironmentObject는 특정 뷰가 외부 데이터에 의존하는 것이므로 데이터 흐름을 추적하기 어려워지기 때문에 테스트와 디버깅이 복잡해질 수 있습니다.
따라서 광범위하게 사용되는 데이터에 한해서만 사용하고, 나머지는 명시적인 방법(ex. @State, @Binding)을 통해주입하는 것이 좋습니다.
EnvironmentObject
는 상위 뷰에서 하위 뷰로 데이터를 전역적으로 공유할 수 있는 프로퍼티 래퍼입니다.ObservableObject
프로토콜을 준수해야 하며, 공유할 데이터는 @Published
프로퍼티로 선언됩니다..environmentObject()
메소드를 통해 주입됩니다.@EnvironmentObject
를 사용하여 주입된 객체에 접근합니다.EnvironmentObject
는 사용하기 전에 상위 뷰에서 반드시 제공되어야 하며, 그렇지 않을 경우 런타임 오류가 발생할 수 있습니다.EnvironmentObject
를 사용하고, 나머지 데이터는 @State
, @Binding
같은 명시적인 방법을 통해 주입하는 것이 좋습니다.출처
https://developer.apple.com/documentation/swiftui/environmentobject
https://medium.com/@Jager-yoo/swiftui-environmentobject-주입의-개념과-사용-시-조심할-점-0ae7747a5fd3
https://ios-development.tistory.com/1161
https://velog.io/@kangciu/SwiftUI-EnvironmentObject를-사용하여-앱-전반에-걸쳐-데이터를-공유하기