오늘은
업무를 진행할때 겪은 어려움을 해결했던 과정에 대해
글을 작성해보려 합니다.
제목 그대로 ViewModel 의 상태변화를 View 에게 알려주고 싶다면
어떻게 해야할까요?
@ObservedObject private var contentViewModel: ContentViewModel
이런식으로 @ObservedObject 를 붙여주게 된다면
ViewModel 의 값이 바뀔때 마다
View 가 알 수 있게 됩니다.
그렇다면, ViewModel 안에 또 다른 ViewModel 의
상태 변화를 알고 싶다면 어떻게 해야할까요?
final class ContentViewModel: ObservableObject {
@ObservedObject var detailViewModel: DetailViewModel
}
전 이전과 같은 방법으로
@ObservedObject 를 detailViewModel 앞에 작성하여
빌드를 진행했습니다.
하지만 의도했던것과는 다르게
View 가 업데이트가 되질 않더라구요.
그 이유는 ContentView 는 ContentViewModel 을 상태변화를
감지할 수 있지만
ContentViewModel 안에서 감지하는 detailViewModel 의
상태변화를 알 수 없기 때문이였습니다.
그렇기 때문에 ContentViewModel 에서
detailViewModel 의 랜덤값이 변화할 때 감지할 수 있는
Publisher 를 하나 만들어
View 업데이트 문제를 해결했습니다.
final class ContentViewModel: ObservableObject {
var detailViewModel: DetailViewModel
private var cancellables: Set<AnyCancellable>
init(detailViewModel: DetailViewModel) {
self.detailViewModel = detailViewModel
cancellables = .init()
bind()
}
private func bind() {
detailViewModel
.$randomValue
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
self?.objectWillChange.send()
}
.store(in: &cancellables)
}
}
sink 를 사용하여 randomValue 가 변화하면
objectWillChange 메서드를 호출하게 되는데
이는 ContentView 에서
"@ObservedObject private var contentViewModel: ContentViewModel"
로 선언된 contentViewModel 의 변경을 요청하게 됩니다.
따라서 의도했던 대로 ContentView 의 UI 가 업데이트 되는 것을
확인할 수 있습니다.
아래는 작성했지만 의도했던대로 동작하지 않았던 코드와
의도한 대로 동작했던 코드를 올려두도록 하겠습니다.
읽어주셔서 감사합니다!
1. 의도 대로 동작하지 않았던 코드
struct ContentView: View {
@ObservedObject private var contentViewModel: ContentViewModel
init(contentViewModel: ContentViewModel) {
self.contentViewModel = contentViewModel
}
var body: some View {
VStack(spacing: 30) {
Button {
self.contentViewModel.detailViewModel.requestRandomValue()
} label: {
Text("클릭하면 아래의 숫자가 업데이트 됩니다.")
}
Text(String(self.contentViewModel.detailViewModel.randomValue))
}
}
}
final class ContentViewModel: ObservableObject {
@ObservableObject var detailViewModel: DetailViewModel
init(detailViewModel: DetailViewModel) {
self.detailViewModel = detailViewModel
}
}
final class DetailViewModel: ObservableObject {
@Published var randomValue = Int.random(in: 1...100)
func requestRandomValue() -> Int {
randomValue = Int.random(in: 1...100)
print("\(randomValue)")
return randomValue
}
}
2. 의도 대로 동작한 코드
struct ContentView: View {
@ObservedObject private var contentViewModel: ContentViewModel
init(contentViewModel: ContentViewModel) {
self.contentViewModel = contentViewModel
}
var body: some View {
VStack(spacing: 30) {
Button {
self.contentViewModel.detailViewModel.requestRandomValue()
} label: {
Text("클릭하면 아래의 숫자가 업데이트 됩니다.")
}
Text(String(self.contentViewModel.detailViewModel.randomValue))
}
}
}
final class ContentViewModel: ObservableObject {
var detailViewModel: DetailViewModel
private var cancellables: Set<AnyCancellable>
init(detailViewModel: DetailViewModel) {
self.detailViewModel = detailViewModel
cancellables = .init()
bind()
}
private func bind() {
detailViewModel
.$randomValue
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
self?.objectWillChange.send()
}
.store(in: &cancellables)
}
}
final class DetailViewModel: ObservableObject {
@Published var randomValue = Int.random(in: 1...100)
func requestRandomValue() -> Int {
randomValue = Int.random(in: 1...100)
print("\(randomValue)")
return randomValue
}
}
'Combine' 카테고리의 다른 글
[Combine] ViewModel 에서 Input, Output 구현하기 (0) | 2024.05.06 |
---|---|
[Combine] SwiftUI 에서는 Publisher 를 어떻게 구독할까? (0) | 2023.10.08 |
[Combine] Subject 알아보기 (PassthroughSubject / CurrentValueSubject) (0) | 2023.10.05 |
[Combine] Publisher 와 Subscriber 는 어떻게 연결할 수 있을까? (0) | 2023.09.28 |
[Combine] Publisher / Operator / Subscriber 알아보기 (0) | 2023.09.27 |