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