본문 바로가기
iOS/SwiftUI

@StateObject vs @ObservedObject

by 소토리텔러 2024. 5. 13.

View와 Logic을 분리하기 위해 MVVM (Model - View - ViewModel) 패턴을 사용하는데

ViewModel은 Logic을 담당하여 View에서 사용할 데이터를 가공하는 역할을 한다. 

 

SwiftUI에서는 ViewModel을 클래스로 구현하고 ObservableObject 프로토콜을 채택한다. 

내부에는 View에서 사용할 데이터가 있어 @Published 프로퍼티로 해당 값의 변화를 알리게 되고

메서드로 데이터를 가공할 수 있도록 한다. 

 

class MyViewModel: ObservableObject {
  @Published var myData: [MyModel] = []
  
  func insert(data: MyModel) {
    myData.append(data)
  }
  
  func delete(index: IndexSet) {
    myData.remove(atOffsets: index)
  }
}

 

View 에서는 이 ViewModel을 이용해서 데이터를 보여주는데 이때 ViewModel 내부의 데이터가 변경될 수 있음을 알리고 관찰하도록 @StateObject 또는 @ObservedObject 프로퍼티를 붙여준다. 

 

struct MyView: View {

  @StateObject var myViewModel: MyViewModel = MyViewModel()
  @ObservedObject var myViewModel: MyViewModel = MyViewModel()

  var body: some View {
    Text("어떤 프로퍼티를 사용해야 될까?")
  }
}

 

이 강의에서는 뷰를 처음 생성하거나 초기화할 때 @StateObject를 사용하고 하위 뷰에서 @ObservedObject를 사용하라고 한다. @StateObject는 View가 다시 그려지더라도 데이터가 보존되지만 @ObservedObject는 View와 함께 제거되었다가 다시 생성되어 데이터가 유지되지 않기 때문이다. 이 내용을 다른 강의에서는 예제를 통해 명확하게 보여주고 있다.

 

class CounterViewModel: ObservableObject {
    @Published var count: Int = 0
    
    init() {
        print("Counter ViewModel initializer!")
    }
    
    func increaseCount() {
        count += 1
    }
}

struct MainView: View {
    @State private var randomNumber: Int = 0
    
    var body: some View {
        VStack(spacing: 20) {
            VStack {
                Text("Random Number: \(randomNumber)")
                Button("Get random number") {
                    randomNumber = (1...100).randomElement()!
                }
            }
            
            CompareView()
        }
    }
}

struct CompareView: View {
    @ObservedObject var viewModel = CounterViewModel()
//    @StateObject var viewModel = CounterViewModel()
    
    init() {
        print("CompareView init")
    }
    
    var body: some View {
        VStack {
            Text("Count: \(viewModel.count)")
            Button("Get ViewModel Count") {
                viewModel.increaseCount()
            }
        }
    }
}

 

@ObservedObject 사용

 

 

 

MainView에서 Get random number 버튼을 누르면 @State 프로퍼티인 randomNumber 값이 바뀌면서 전체 MainView를 다시 그리게 된다. 이때 MainView 내부에 있는 CompareView도 다시 그리게 되면서 @ObservedObject 프로퍼티인 viewModel 인스턴스가 다시 생성되면서 viewModel 내부의 count 값이 0으로 초기화되는 것을 알 수 있다. 

 

 

@StateObject 사용

 

 

위와 다르게 @StateObject 프로퍼티를 사용하면 count 값이 유지되면서 randomNumber 값이 변하는 것을 알 수 있다. viewModel 인스턴스가 매번 다시 생성되는지는 initializer에 로그를 남겨 확인해보는 것이 정확!

'iOS > SwiftUI' 카테고리의 다른 글

LazyVGrid  (0) 2024.05.03
Lifecycle event modifier  (0) 2023.11.23
NavigationStack  (0) 2023.11.21
ProgressView  (0) 2023.11.21
TextField & SecureField  (0) 2023.11.21