[Swift] @Environment에 대해 알아보기(Property Wrapper 3편)

2024. 1. 20. 16:17·iOS&Swift

안녕하세요.

이전 글에서 @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에 대해 알아보기 마무리하겠습니다.

저작자표시 (새창열림)

'iOS&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
'iOS&Swift' 카테고리의 다른 글
  • [Swift] @ObservedObject와 @StateObject의 차이에 대해 알아보기(Property Wrapper 2편)
  • [Swift] @State, @Binding, @Published 대해 알아보기(Property Wrapper 1편)
  • [Swift] Alamofire의 RequestInterceptor을 사용해 토큰 갱신하기
  • Combine 뽀개기 4장: Scheduler
Esiwon
Esiwon
iOS 개발 블로그
  • Esiwon
    시원한 코드 기록
    Esiwon
  • 전체
    오늘
    어제
    • 분류 전체보기 (70)
      • iOS&Swift (24)
      • git & github (1)
      • 코테 (41)
      • 네부캠 회고 (4)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • 깃허브
  • 공지사항

  • 인기 글

  • 태그

    코테
    백준
    코딩테스트
    브루트포스 알고리즘
    Property wrapper
    이분탐색
    구현
    Race Condition
    dfs
    Swift
    동시성
    ios
    photoUI
    photos
    다이나믹 프로그래밍
    PhotoKit
    그리디
    탐색
    재귀
    챌린지
    실버
    BFS
    노드
    GCD
    회고
    네부캠
    알고리즘
    비동기
    완전탐색
    Combine
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Esiwon
[Swift] @Environment에 대해 알아보기(Property Wrapper 3편)
상단으로

티스토리툴바