XLOG

[SwiftUI] Environment 활용하기 본문

Swift/SwiftUI

[SwiftUI] Environment 활용하기

X_PROFIT 2024. 6. 25. 16:15

1. Environment 란 무엇인가?

property wrapper 로 view의 environmentValue 를 읽는데 사용할 수 있다. 

그렇다면 EnvironmentValues 는 무엇일까?

view 계층을 통해 값을 전달해주는 역할을 한다. 이는 preferenceValue 와 반대되는 개념이다.

https://developer.apple.com/wwdc24/10146

2. 사용 방법

// 정의

struct MyEnvironmentKey: EnvironmentKey {
    static var defaultValue: Bool = false
}

extension EnvironmentValues {
    var myEnvironmentValue: Bool {
        get { self[MyEnvironmentKey.self] }
        set { self[MyEnvironmentKey.self] = newValue }
    }
}


// 사용

struct ContentView: View {
	var body: some View {
    	ChildView()
//        	.environment(\.myEnvironmentValue, { 값 설정, true or false })  
            .environment(\.myEnvironmentValue, false)
    }
}

strut ChildView: View {
	@Environmnet(\.myEnvironmentValue) var myEnvironmentValue
    
    var body: some View {
    	if myEnvironmentValue {
        	Text("True")
        } else {
            Text("False")
        }
    }
}

EnvironmentKey 정의 -> EnvironmentValues 의 property 정의 ->  하위View에 environment 넘겨서 사용

3. 응용

사실 처음 이걸 접했을 때 어떻게 사용을 하는게 좋을지 와닿지 않았다. 근데 최근 Building REUSEABLE SwiftUI Component 영상을 봤다. 회사에서 근무하며 DesignSystem 을 모듈화 하여 반복되는 Design Component 를 만드는데 비슷하면서 여러 조건들로 조금씩 디자인이 달랐고, 그걸 충족하기 위하여 해당 Component 에는 초기화 당시 많은 프로퍼티들이 필요로 했다. 타입이 8가지라고 가정을 하게 되면 8가지를 하나의 프로퍼티로 구분하는 것이 아닌 여러 프로퍼티로 구분을 해야하기 때문이었다. 하지만 주로 사용하는 요소는 정해져 있었고, 가끔 있는 case 로 인하여 계속해서 많은 프로퍼티들을 넘겨주었다. 하지만 이 Environment 와 ViewModifier를 통한다면 불필요한 프로퍼티를 줄일 수 있다.

예를 들어 Custom 한 Text View에 Text 부분만 Background Color를 넣고 싶다고 가정을 해보자.

import SwiftUI

struct CustomTextView: View {
    
    @Binding var text: String
    
    let textBackgroundColor: Color
    
    var body: some View {
        Text(text)
            .background(textBackgroundColor)
            .padding(16)
            .background{
                Rectangle()
                    .stroke(lineWidth: 2)
                    .fill(.red)
            }
    }
}

#Preview {
    CustomTextView(text: .constant("Hello"), textBackgroundColor: .blue)
}

단순하게 생각하면 이런식으로 했을 것이다, 하지만 만약 Environment를 사용한다면..?

struct TextBackgroundColor: EnvironmentKey {
	// default 색상 정의
    static var defaultValue: Color = .blue
}

extension EnvironmentValues {
    var textBackgroundColor: Color {
        get { self[TextBackgroundColor.self] }
        set { self[TextBackgroundColor.self] = newValue }
    }
}

// ViewModifier 정의
extension View {
    func textBackgroundColor(_ color: Color) -> some View {
        environment(\.textBackgroundColor, color)
    }
}

struct CustomTextView: View {
    
    @Environment(\.textBackgroundColor) var textBackgroundColor
    
    @Binding var text: String

    var body: some View {
        Text(text)
            .background(textBackgroundColor)
            .padding(16)
            .background{
                Rectangle()
                    .stroke(lineWidth: 2)
                    .fill(.red)
            }
    }
}

#Preview {
    CustomTextView(text: .constant("Hello"))
        .textBackgroundColor(.red)
}

#Preview {
    CustomTextView(text: .constant("Hello"))
}

이렇게 대부분 default 값이 있고 중간에 Custom한 View 내부에서 변경을 하는 값이 아니라면 ViewModifer를 통해 environmentValue를 변경하여 넘겨줄 수 있다. default 값을 사용하고 싶을 때는 그냥 View 만 가져다 사용할 수 있다. 이로인해 초기화에 불필요한 프로퍼티를 줄일 수 있다.

4. 느낀점

처음 SwiftUI 를 접하고 State, Binding 프로퍼티 Wrapper 를 통해 쉽게 데이터 흐름을 제어할 수 있었고, 불편함을 느낄 때는 ObservableObject, EnvironmentObject 를 사용했었는데, 데이터의 흐름을 제어할 수 있는 방법은 여러가지고 앱의 성능을 최적화 하기 위해선 불필요한 데이터를 전달받는 것을 줄여야 했다. 최근 WWDC 24 에 ContainerValues 도 추가가 되었는데, 이러한 요소들을 잘 활용해야 같이 일하는 직원들이 사용하기 편한 컴퍼넌트를 만들 수도 있고, 앱의 성능 최적화도 가능한 것 같다.

 

참고영상: https://youtu.be/PocljzAYFL4?si=Wb0779Lk8Rw8jOak