안녕하세요.
이번에는 Environment에 대해 알아볼게요.
## Environment
Environment property wrapper를 사용하면 SwiftUI 뷰의 환경에 저장된 값을 읽을 수 있어요.
Environment 프로퍼티를 선언할 때 아래처럼 EnvironmentValues key path를 사용해서 어떤 값을 읽을지 결정합니다.
@Environment(\.colorScheme) var colorScheme: ColorScheme |
뷰 환경 또는 앱 환경에 따라 다른 뷰를 그리고 싶은 경우 아래처럼 조건문을 달아줄 수도 있어요.
if colorScheme == .dark { // Checks the wrapped value. | |
DarkContent() | |
} else { | |
LightContent() | |
} |
만약 Environment 값이 변경이 되면, SwiftUI가 알아서 이와 관련된 모든 뷰를 업데이트해주니 크게 걱정할 필요 없습니다.
그럼 Environment 프로퍼티는 언제 사용하면 좋을까요??
뭐... 여러 관점이 있겠지만, 애플은 앱 전체에 값을 배포해야 하는 경우에 Environment에 저장해서 사용하면 좋다고 합니다.

Environment 프로퍼티를 선언했을 때는 읽기(read)만 가능하구요,
environment(_:_:) 수식어를 사용하면 값을 설정(write)할 수 있어요.
struct ContentView: View { | |
var body: some View { | |
VStack { | |
ChildView() | |
.environment(\.multilineTextAlignment, .center) ✅ | |
} | |
} | |
} | |
struct ChildView: View { | |
var body: some View { | |
Text("Child View\nGoooooooood") | |
} | |
} |

## Environment 상속
부모 뷰에서 Environment 값을 수정해서 자식 뷰에 전달할 경우, 부모 뷰 하위에 있는 모든 뷰는 수정된 값이 자동으로 설정됩니다.
예를 들어볼까요?
struct ContentView: View { | |
var body: some View { | |
VStack { | |
ChildView() | |
.environment(\.multilineTextAlignment, .center) | |
} | |
} | |
} | |
struct ChildView: View { | |
var body: some View { | |
VStack { | |
Text("Child View\nGoooooooood") | |
Divider() | |
ChildChildView() | |
} | |
} | |
} | |
struct ChildChildView: View { | |
var body: some View { | |
Text("Child Child View\nGoooooooood2") | |
} | |
} |
부모 뷰에서 multilineTextAlignment 값을 수정해서 ChildView에 전달해주니, ChildView의 하위 뷰인 ChildChildView에서도 수정된 값이 적용된 것을 볼 수 있어요.

## Custom Environment Value
여러 시스템 환경값을 가져올 수 있는 EnvironmentValues들이 있는 것처럼, 당연히 커스텀 EnvironmentValue도 만들 수 있겠죠?
커스텀 EnvironmentValue를 만들기 위해서는 EnvironmentKey를 사용해서 새로운 key를 선언하고, EnvironmentValues 구조체를 확장(extension)해서 새로운 프로퍼티를 선언해주면 됩니다.
직접 해볼까요??
우선 EnvironmentKey 프로토콜을 채택한 커스텀 키 타입을 선언하고 그 안에 defalutValue 프로퍼티를 추가해줍니다.
private struct MyEnvironmentKey: EnvironmentKey { | |
static let defaultValue: String = "Default value" | |
} |
EnvironmentKey 프로토콜은 아래와 같은 구조로 되어 있는데요,
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) | |
public protocol EnvironmentKey { | |
/// The associated type representing the type of the environment key's | |
/// value. | |
associatedtype Value | |
/// The default value for the environment key. | |
static var defaultValue: Self.Value { get } | |
} |
Swift 컴파일러는 defaultValue에 설정된 값을 가지고 자동으로 associatedtype Value의 타입을 추론합니다.
(참고로 알아두시면 좋을 것 같아요ㅎㅎ)
그다음, EnvironmentValues를 확장해서 새로운 environment value 프로퍼티를 선언해주면 끝이에요!
extension EnvironmentValues { | |
var myCustomValue: String { | |
get { self[MyEnvironmentKey.self] } | |
set { self[MyEnvironmentKey.self] = newValue } | |
} | |
} |
커스텀 EnvironmentValue를 만든 뒤에는 @Environment property wrapper로 읽을 수 있고, environment(_:_:) 수식어를 사용해서 값을 override 해줄 수 있습니다.
struct ContentView: View { | |
@Environment(\.myCustomValue) var myCustomValue | |
var body: some View { | |
VStack { | |
Text(myCustomValue) | |
ChildView() | |
.environment(\.myCustomValue, "Another value") | |
} | |
} | |
} | |
struct ChildView: View { | |
@Environment(\.myCustomValue) var myCustomValue | |
var body: some View { | |
Text(myCustomValue) | |
} | |
} |

사용성을 높이기 위해서 Environment value를 수정하는 새로운 수식어를 정의해주기도 하니 참고해주세요.
extension View { | |
func myCustomValue(_ myCustomValue: String) -> some View { | |
environment(\.myCustomValue, myCustomValue) | |
} | |
} | |
struct ContentView: View { | |
@Environment(\.myCustomValue) var myCustomValue | |
var body: some View { | |
VStack { | |
Text(myCustomValue) | |
ChildView() | |
.myCustomValue("Another value") // ✅ | |
} | |
} | |
} | |
struct ChildView: View { | |
@Environment(\.myCustomValue) var myCustomValue | |
var body: some View { | |
Text(myCustomValue) | |
} | |
} |
## 참고
- https://developer.apple.com/documentation/swiftui/state-and-data-flow
- https://developer.apple.com/documentation/swiftui/environment
- https://developer.apple.com/documentation/swiftui/environmentvalues
- https://developer.apple.com/documentation/swiftui/environmentkey
- https://swiftwithmajid.com/2019/08/21/the-power-of-environment-in-swiftui/
이번 글은 여기서 마무리.
'SwiftUI' 카테고리의 다른 글
trim (0) | 2022.06.06 |
---|---|
PreferenceKey (0) | 2022.05.30 |
SwiftUI에서 ViewController를? (0) | 2022.05.11 |
복잡한 Navigation Flow 처리 (feat. Combine) (0) | 2022.05.10 |
MotionScape (0) | 2022.05.06 |