SwiftUI

Environment

Phililip 2022. 5. 29.
728x90

안녕하세요.

 

이번에는 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()
}
view raw Test.swift hosted with ❤ by GitHub

 

 

만약 Environment 값이 변경이 되면, SwiftUI가 알아서 이와 관련된 모든 뷰를 업데이트해주니 크게 걱정할 필요 없습니다.

 

 

 

그럼 Environment 프로퍼티는 언제 사용하면 좋을까요??

 

뭐... 여러 관점이 있겠지만, 애플은 앱 전체에 값을 배포해야 하는 경우에 Environment에 저장해서 사용하면 좋다고 합니다.

 

출처: https://developer.apple.com/documentation/swiftui/state-and-data-flow

 

 

 

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