SwiftUI의 Environment

byron1st·2021년 9월 5일
1

Swift 배우기

목록 보기
3/3

Core Data를 View에서 사용하기 위해, managedObjectContext 라는 Environment를 지정해주자.

... 뭐라고?

정말 무슨 말인지 하나도 모르겠기 때문에, 하나하나 뜯어보자.

Environment

우선 SwiftUI에서 Environment 라는 것은 Property Wrapper 라는 특수한 형태의 구조체이다. 구조체라는 말은 여러 프로퍼티 값들을 갖고 있다는 말인데, Environment는 말 그대로 View의 "환경"과 관련된 여러 값들을 갖고있다. 예를 들면, colorScheme, pixelLength, locale 과 같은 것들이다. 모든 리스트는 EnvironmentValues 문서를 참조하자.

React 에서 온 개발자들이라면, Redux 같은 글로벌 스테이트 관리를 생각해보면 된다. 실제로 React 앱을 개발할 때, Color Scheme이나 Locale 같은 건 Redux 등으로 앱 전반에 걸친 글로벌 스테이트로 넣고 관리하기도 하는데, 같은 개념으로 이해하면 된다.

다시 돌아와서, Environment는 View 와 관련된 여러 변수들을 View 계층 구조에 전반에 걸쳐 보급하는 역할을 한다. 그래서 상위 View 에서 environment() 함수를 통해 Environment Value를 지정해주면, 하위 View에서 적당히 꺼내 쓸 수 있다. 이때, Environment Value 내용이 변경 되면, 자동으로 그 변경이 전파된다.

좋다. 그럼 Property Wrapper 는 무엇인가.

Property Wrapper

Property Wrapper 는 프로퍼티의 반복적인 get, set 행위를 재사용하기 위한 구조체이다. ... 이게 무슨 말인가 알아보기 위해, 공식 문서의 Property Wrapper 예제를 살펴보자. (링크로 가서 적당히 내려보면 Property Wrapper 섹션이 있다. 섹션 링크도 제공하지 않다니... 실망이야)

@propertyWrapper
struct TwelveOrLess {
    private var number = 0
    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, 12) }
    }
}

TwelveOrLess 라는 Property Wrapper 구조체는 Int 타입의 프로퍼티에 대한 get, set 에 대한 아래와 같은 행위를 'Wrapping' 하고 있다.

  • get : 그냥 같은 값을 반환
  • set : 입력값이 12보다 크면, 12를 할당. 아니면 입력값을 할당.

즉, TwelveOrLess 로 "Wrapping" 된 Int 타입의 프로퍼티는 얼마의 값이 할당되던 간에 최대 12까지만 갖을 수 있다.

struct SmallRectangle {
	@TwelveOrLess var height: Int
    @TwelveOrLess var width: Int
}

var rectangle = SmallRectangle()
print(rectangle.height)
// Prints "0"

rectangle.height = 10
print(rectangle.height)
// Prints "10"

rectangle.height = 24
print(rectangle.height)
// Prints "12"

Property Wrapper 는 프로퍼티의 get, set 행위 중 반복적으로 사용하는 행위를 구조체 형태로 정의하여, 여기저기 재사용할 수 있도록 해주는데 있다. 만약 이런 문법이 없다면, "최대 12 까지만 할당될 수 있음"에 해당하는 set 코드를 heightwidth 각각에 중복해서 작성해주어야 했을 것이다.

Core Data의 managedObjectContext

자 이제 다시 처음으로 돌아와서, Core Data를 View에서 사용하기 위해, managedObjectContext 라는 Environment를 지정해주자. 여기서 Environment 는 Property Wrapper 이다.

@main
struct DoneListApp: App {
    let persistentController = PersistenceController.shared
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.managedObjectContext, persistentController.container.viewContext)
        }
    }
}

EnvironmentEnvironmentValues 는 여러가지가 있지만, 우리가 사용할 것은 managedObjectContextNSPersistentContainerviewContext 값을 할당해주면 된다. colorScheme 등과는 다르게, managedObjectContext는 우리가 외부에서 별도로 주입해주어야 하는데, 이를 위한 함수가 View 구조체의 environment 함수다.

ContentView().environment(\.managedObjectContext, persistentController.container.viewContext)

여기서 \.managedObjectContextEnvironmentValues 구조체의 managedObjectContext 프로퍼티를 의미한다. ...어째서냐고? 이게 바로 Key Path Expression 이라는 문법이다. (하아...)

Key Path Expression

Key Path Expression은 런타임 중 특정 타입의 객체가 파라미터로 들어왔을 때, 해당 타입의 특정 프로퍼티에 대한 경로를 표현해주는 것을 의미한다.

즉, 우리가 명시적으로 "어떤 변수(객체)"의 "이 프로퍼티"를 써주세요 하고 코드에 입력할 수 있을 것이고, 이게 보통의 코딩이다. 하지만, 코드를 작성하는 순간에는 어떤 객체(변수)가 들어올지 모를 경우, 프로퍼티로 향하는 경로만 입력해줌으로써, 런타임 중 어떤 객체가 들어오던지같에 타입만 맞으면 그 특정 프로퍼티를 꺼내서 쓸 수 있게 해주는 것이다.

위 코드에서 \.managedObjectContext 는 사실 \EnvironmentValues.managedObjectContext 의 축약이다. EnvironmentValues 타입에 대해 타입 인퍼런스가 가능해서 생략된 것이다. 즉, 위 코드는 EnvironmentValues 타입의 객체가 일단 들어오면, 함수에서는 .managedObjectContext 에 있는 프로퍼티 값을 쓰라는 의미다.

다시 돌아와서, environment(_ keyPath:, _ value:) 함수는 해당 keyPath 에 해당 valueset 하라는 의미이다. 그래서 내가 만든 NSPersistentContainer 타입 객체의 viewContext 값을 EnvironmentValues.managedObjectContext에 할당하게 된다.

하위 View 에서 꺼내 쓰기

struct ContentView: View {
    @Environment(\.managedObjectContext) var context
	...
}

하위 View 들에서는 위 코드와 같이 @Environment 를 통해, 내가 주입한 값을 꺼내 쓸 수 있다.

profile
Fullstack software engineer specialized for Blockchain

0개의 댓글