[Swift]ObservedObject and StateObject

👉🏻 스위프트에서 @ObservedObject와 @StateObject에 대한 설명입니다.
Here’s an explanation of @ObservedObject and @StateObject in Swift.

👉🏻 @ObservedObject
✔️ @ObservedObject는 구조체의 초기화 시점에 계속해서 인스턴스(프로퍼니 초기화)를 다시 실행 합니다.
@ObservedObject continually re-executes the instance (property initialization) at the time of struct initialization.

✔️ @ObservedObject는 부모뷰에서 전달받은 뷰 모델을 관찰만 합니다.
(관찰이란 뷰모델[Class UserSettings] 값이 변하면 [@Published로 선언된 변수의 값])UI를 업데이트 하는것을 의미합니다.)
@ObservedObject only observes the view model passed from the parent view. (Observation means that when the value of the view model [class UserSettings] changes, the UI is updated [the value of the variable declared with @Published].)

👉🏻 @StateObject
✔️@StateObject는 SwiftUI의 state storage(뷰의 라이프 싸이클 동안 보관되는 저장소)에
인스턴스를 저장하고, 처음 뷰가 생성될 때만 초기화하도록 보장합니다.
@StateObject stores an instance in SwiftUI’s state storage (which is kept throughout the view’s lifecycle) and ensures that it is initialized only when the view is first created.

✔️ @StateObject는 뷰모델의 인스턴스를 생성하고 관리하며 부모뷰에서 사용합니다.
@StateObject creates and manages an instance of the view model and uses it in the parent view.

👉🏻 아래의 코드를 실행해보면 차이를 확인 할 수 있습니다.
You can see the difference by running the code below.

👉🏻@StateObject를 사용할 위치에 @ObservedObject를 사용하면 init()이 두번 실행되는 문제가 발생합니다.
If you use @ObservedObject where you would use @StateObject, you will run into the problem that init() is executed twice.

👉그래서 @StateObject를 사용하면 이런 문제를 방지 할 수 있습니다.
So using @StateObject can help us avoid this problem.

👉🏻코드/code
✔️ ContentView.swift


import SwiftUI
import Combine // add Combine


class MyViewModel: ObservableObject {
    @Published var data = ""
    
    init() {
        print("MyViewModel init")
        fetchData() // 네트워크 호출 같은 작업
    }
    func fetchData() {
        print("fetchData called")
        data = "Data Updated..."
    }
}

struct ContentView: View {
@State private var count = 0
   @ObservedObject var vm = MyViewModel() // 오류 / error
    //@StateObject var vm = MyViewModel() // 정상 / normal
    
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text("Data:\(vm.data)")
            
            Button("Update view") {
                count += 1
                print(count)
                vm.fetchData()
            }
            Button("Update Clear") {
                count = 0
                vm.data = "Clear"
                print(count)
            }
            
        }
        .padding()
    }
}

#Preview {
    ContentView()
}

✔️ RootView.swift

import SwiftUI

struct RootView: View {
    var body: some View {
        NavigationStack {
            VStack(spacing: 20) {
                NavigationLink("Go to ContentView again") {
                    ContentView()   // 뷰 재생성 / Regenerate view
                }

                ContentView()      
                    .padding()
            }
            .navigationTitle("RootView")
        }
    }
}

#Preview {
    RootView()
}

👉🏻 스크린 샷 / ScreenShot

Leave a Reply