본문 바로가기
카테고리 없음

[SwiftUI] StateObject, ObservedObject, EnvironmentObject

by 붕어사랑 티스토리 2024. 3. 21.
반응형

1. StateObject, ObservedObject란

  • 값이 아닌 참조타입(클래스) 형태의 State를 관리할 때 사용된다.
  • private으로 선언되어야 한다. memberwise initializer에서 설정되는걸 막기위한것
  • @Published 프로퍼티 래퍼를 사용한다
  • ObservableObject 프로토콜을 따른다 
  • 처음 한번 생성되면 뷰 라이프사이클 상관없이 계속해서 유지된다.

처음 이걸보고 struct로 이루어진 State와 뭐가다르지? 라고 생각하고 자료를 찾아봤는데... 명확하게 답변해주는 토픽이 하나도 없었다.

단지 class는 reference 타입이라는 점이 가장 큰 차이.

 

그 외에도 class가 struct 대비 가지고 있는 장점들이 워낙 많으니 그러한 차이들이 struct state와 stateObject의 차이를 두는 것 같다.

그리고 참조타입이기에 매번 바인딩 키워드를 안쓰고 자식뷰에 전달 가능하다는 점 정도?

class NetworkModel: ObservableObject {
    @Published var data: Data?
    @Published var isLoading = false
    @Published var error: Error?

    func loadData() {
        self.isLoading = true
        // 네트워크 요청을 수행하고 결과를 처리합니다...
    }
}

struct ContentView: View {
    @StateObject var model = NetworkModel()

    var body: some View {
        Button("Load Data") {
            model.loadData()
        }
    }
}

 

 

 

2. ObservedObject

 

기본적으로 StateObejct와 똑같이 사용 가능하다. 대신 ObservedObject는 뷰가 다시 그리지면 다시 생성된다.

 

 

허나 애플 공식문서에서는 ObservedObject를 아래와 같이 사용 권장한다

 

Don’t specify a default or initial value for the observed object. Use the attribute only for a property that acts as an input for a view, as in the above example.

 

대충 사용할때 Binding의 StateObject버전 처럼 쓰라는 뜻. 아래처럼 부모의 StateObject를 받아올때 쓰라는 얘기이다.

class DataModel: ObservableObject {
    @Published var name = "Some Name"
    @Published var isEnabled = false
}


struct MyView: View {
    @StateObject private var model = DataModel()


    var body: some View {
        Text(model.name)
        MySubView(model: model)
    }
}


struct MySubView: View {
    @ObservedObject var model: DataModel


    var body: some View {
        Toggle("Enabled", isOn: $model.isEnabled)
    }
}

 

 

 

 

 

3. EnvironmentObject

flutter의 프로바이더와 비슷하다. 최상위뷰에 StateObject를 EnvironmentObject로 설정하면 하위 뷰에서 전역적으로 접근 가능하다.

 

아래 예제에는 ContentView에 EnvironmentObject를 붙였지만 제일 좋은 관습은 WindowGroup에 붙이는게 가장 좋다.

 

import SwiftUI

// ObservableObject 프로토콜을 준수하는 클래스 정의
class UserData: ObservableObject {
    @Published var score = 0 // 게임 점수를 저장하는 속성
}

// EnvironmentObject를 사용하여 UserData를 전역으로 사용할 수 있게 함
@main
struct MyApp: App {
    let userData = UserData() // 전역 UserData 객체 생성
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(userData) // ContentView에 전역 UserData 객체 전달
        }
    }
}

// 부모 뷰
struct ContentView: View {
    var body: some View {
        VStack {
            Text("Parent View")
            ChildView() // 자식 뷰 호출
        }
    }
}

// 자식 뷰
struct ChildView: View {
    @EnvironmentObject var userData: UserData // 전역 UserData 객체를 가져옴
    
    var body: some View {
        VStack {
            Text("Child View")
            Text("Score: \(userData.score)")
            Button("Increase Score") {
                // 버튼을 누를 때마다 점수 증가
                userData.score += 1
            }
        }
    }
}
반응형

댓글