안녕하세요.
이전 글에서 @ObservedObject, @StateObject에 대해 알아봤고,
오늘은 마지막 @Environment에 대해 알아보겠습니다.
@Environment
A property wrapper that reads a value from a view’s environment.
출처: 공식문서
공식 문서에는.
@Environment Property Wrapper는 View 환경에 저장된 EnvironmentValues 을 읽어오기 위한 Property Wrapper이고,
key path를 통해 EnvironmentValues을 지정하고, 읽어올 수 있다고, 나와있습니다.
위 같은 글을 읽었을 땐 바로 이해가 되질 못했습니다.
때문에 직접 사용해 보면서 정리해 보겠습니다.
예제를 통해 사용해 볼 EnvironmentValue는 dismiss입니다.
swift가 기본으로 제공하는 다양한 EnvironmentValues 중 하나입니다
struct SubView: View {
@Environment(\.dismiss) var dismiss
var body: some View {
VStack {
Button("dismiss") {
dismiss()
}
}
}
}
위와 같이 dismiss를 @Environment 사용하여 선언해 주고,
struct ContentView: View {
var body: some View {
NavigationStack {
VStack {
NavigationLink {
SubView()
} label: {
Text("SubView로 이동")
}
}
.padding()
}
}
}
상위 View에서 push하면,
위와 같이 정상적으로 dismiss 되는 것을 보실 수 있습니다.
만약, swift가 제공하는 EnvironmentValues 중 필요한 것이 없다고 한다면,
직접 만들어 사용할 수도 있습니다.
private struct SubViewBackgroundColorKey: EnvironmentKey {
static let defaultValue = Color.red
}
extension EnvironmentValues {
var subViewBackgroundColor: Color {
get { self[SubViewBackgroundColorKey.self] }
set { self[SubViewBackgroundColorKey.self] = newValue }
}
}
위와 같이 구조체를 통해 EnvironmentKey protocol을 채택한 Key 타입을 만들어주고, 기본값을 설정합니다,
그다음, EnvironmentValues를 확장하여 원하는 Value를 생성해 줍니다.
struct SubView: View {
@Environment(\.subViewBackgroundColor) var subViewBackgroundColor
var body: some View {
subViewBackgroundColor
}
}
그리고 위와 같이 사용해주면,
끝!!
만약, 기본값이 아닌 다른 값으로 변경해 사용하고 싶다면,
상위 View에서 .environment를 사용하여,
struct ContentView: View {
@State private var isPresentSubView2: Bool = false
var body: some View {
NavigationStack {
VStack {
NavigationLink {
SubView()
.environment(\.subViewBackgroundColor, .yellow) // 값 변경
} label: {
Text("SubView push")
}
}
.padding()
}
}
}
위와 같이 .yellow 값을 넣으면,
위와 같이 SubView에선 변경된 값을 사용할 수 있습니다.
그럼 이번엔 @Environment와 비슷하지만 좀 다른 @EnvironmentObject에 대해 알아보겠습니다.
@EnvironmentObject
A property wrapper type for an observable object that a parent or ancestor view supplies.
출처: 공식문서
@EnvironmentObject 같은 경우 @Environment과 사용법은 비슷하지만, 사용되는 상황은 많이 다릅니다.
@Environment과 다르게 @EnvironmentObject는 상위 View가 하위 View에서 observable한 객체를 넘겨줄 때 사용할 수 있습니다.
예제 코드를 통해 이해해 보겠습니다.
class DataModel: ObservableObject {
@Published private(set) var name = "Some Name"
func changeName(name: String) {
self.name = name
}
}
먼저, 위와 같이 observable이 가능한 객체를 만들어 주고,
struct ContentView: View {
@State private var code: Int = 0
@StateObject private var dataModel = DataModel()
var body: some View {
VStack {
SubView(dataModel: dataModel)
}
.padding()
}
}
struct SubView: View {
@ObservedObject private var dataModel: DataModel
init(dataModel: DataModel) {
self.dataModel = dataModel
}
var body: some View {
VStack {
Text("이름: \(dataModel.name)")
Button("랜덤 이름") {
dataModel.changeName(name: "ABC\(Int.random(in: 1...1000))")
}
}
}
}
위와 같이 코드를 작성해 보았습니다.
위 코드를 @EnvironmentObject를 사용해서 수정해 보도록 하겠습니다.
struct ContentView: View {
@State private var code: Int = 0
@StateObject private var dataModel = DataModel()
var body: some View {
VStack {
SubView()
.environmentObject(dataModel) // init -> environmentObject
}
.padding()
}
}
struct SubView: View {
@EnvironmentObject var dataModel: DataModel // @ObservedObject -> @EnvironmentObject
var body: some View {
VStack {
Text("이름: \(dataModel.name)")
Button("랜덤 이름") {
dataModel.changeName(name: "ABC\(Int.random(in: 1...1000))")
}
}
}
}
위와 같이 수정해 보았습니다.
좀 더 깔끔해 보이는 것 같네요.
둘 중 정답이 없기 때문에 자신만의 스타일로 코드를 작성하면 될 것 같습니다.
코드가 잘 작동하는지 봐볼까요?
정상 작동하는 것을 보실 수 있습니다.
여기까지 3편에 걸친 Property Wrapper에 대해 알아보기 마무리하겠습니다.
'Swift' 카테고리의 다른 글
[Swift] @ObservedObject와 @StateObject의 차이에 대해 알아보기(Property Wrapper 2편) (0) | 2024.01.20 |
---|---|
[Swift] @State, @Binding, @Published 대해 알아보기(Property Wrapper 1편) (0) | 2024.01.19 |
[Swift] Alamofire의 RequestInterceptor을 사용해 토큰 갱신하기 (0) | 2024.01.18 |
Combine 뽀개기 4장: Scheduler (0) | 2023.07.26 |
Combine 뽀개기 3장: Subject (0) | 2023.07.26 |