본문 바로가기

iOS/SwiftUI

[SwiftUI] EnvironmentObject / ObservedObject / StateObject

EnvironmentObject, ObservedObject, StateObject 세가지의 특징을 간단한 예제를 통해 알아보자

class ViewModel: ObservableObject {
    @Published var count = 0
}
@main
struct [YourAppName]: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(ViewModel())
        }
    }
}
struct ContentView: View {
    @EnvironmentObject var environment: ViewModel
    
    var body: some View {
        VStack {
            AView(observed: ViewModel())
            Button("\(environment.count)") {
                environment.count += 1
            }
        }
    }
}

struct AView: View {
    @ObservedObject var observed: ViewModel
    
    var body: some View {
        VStack {
            BView(state: ViewModel())
            Button("\(observed.count)") {
                observed.count += 1
            }
        }
        .frame(width: 300, height: 300, alignment: .top)
        .background(Color.yellow)
    }
}

struct BView: View {
    @StateObject var state: ViewModel
    
    var body: some View {
        VStack {
            CView()
            Button("\(state.count)") {
                state.count += 1
            }
        }
        .frame(width: 200, height: 200, alignment: .top)
        .background(Color.green)
    }
}

struct CView: View {
    @EnvironmentObject var environment: ViewModel
    
    var body: some View {
        VStack {
            Button("\(environment.count)") {
                environment.count += 1
            }
        }
        .frame(width: 100, height: 100)
        .background(Color.orange)
    }
}

바깥부터 @EnvironmentObject, @ObservedObject, @StateObject, @EnvironmnetObject를 사용했다.

간단하게 버튼을 누르면 1씩 증가하는 예제인데 하나의 ObservableObject를 채택한 ViewModel을 @EnvironmentObject, @ObservedObject, @StateObject를 사용해 비교해보자.

 

@EnvironmentObject

먼저 가장 바깥의 버튼과 가장 안쪽의 버튼은 하나의 @EnvironmentObject를 바라보고 있으므로 어떤 것을 클릭해도 두 개의 값이 동시에 변하는 것을 볼 수 있다.

가장 부모 뷰에 .environmentObject(ViewModel())함수를 통해 주입을 해주면 하위 뷰 어느 곳에서나 따로 전달을 하지 않아도 사용할 수 있다.

 

@ObservedObject

노란색 뷰의 버튼은 @ObservedObject를 사용했는데 부모 뷰가 다시 렌더링 될 때마다 @ObservedObject도 다시 주입을 하여 count값이 초기 값으로 바뀌는 것을 볼 수 있다.

+) 만약 부모 뷰가 리 렌더링 되지 않는다면 @ObservedObject의 값도 초기화가 안될까??

기존에는 주황색 뷰의 버튼을 클릭하면 @EnvironmentObject 값이 변경 > ContentView의 값이 변경 되면서 리 렌더링 > 자식 뷰의 뷰모델을 다시 주입하여 값이 초기화가 되었지만,

ContentView의 @EnvironmentObject코드를 모두 주석 처리하고 다시 빌드하여 테스트했을 때 주황색 뷰의 버튼을 클릭해도 부모 뷰를 다시 그리지 않아 @ObservedObject값도 초기화가 안되었다.

 

@StateObject

@ObservedObject의 단점을 보완하기 위해 SwiftUI2에서 추가된 @StateObject이다. 초록색 뷰의 버튼은 @StateObject를 사용하였는데 부모 뷰가 렌더링 되어도 기존 값을 그대로 유지하고 있다.