기어가더라도 제대로
[SwiftUI-기초] @Published 프로퍼티의 데이터 동기화 하는 방법 본문
상황 설명
뷰 컨트롤러에서 뷰 모델의 스트림을 구독을 하고 뷰에서 이벤트를 트리거했을 때, 이벤트 스트림 내부에서 VM의 같은 프로퍼티를 참조했을 때 값이 나타나질 않았다. 같은 Source of Truth 인데 이런 차이가 발생하는 이유가 무엇일까?
import Combine
import UIKit
class FooViewModel: ObservableObject {
@Published var data: String?
func setData() {
data = "New Data"
}
}
class FooViewController: UIViewController {
let vm: FooViewModel
var cancellables = Set<AnyCancellable>()
init(vm: FooViewModel) {
self.vm = vm
}
override func viewDidLoad(_ animated: Bool) {
super.viewDidLoad(animated)
// MARK: - 문제 지점
// observe
vm.$data
.sink { [weak self] data in
print(data) // optional("New Data")
print(vm?.data) // optional(nil)
}
.store(at: cancellables)
}
@IBAction
private lazy var buttonTouched() {
vm.setData()
}
}
그러면 어떻게 업데이트 되는 값을 전달 할 수 있을까?
핸들링 함수에 매개변수로 업데이트 되는 값 전달하기
// observe 함수 내부
vm.$data
.sink { [weak self] received in
self?.handleUpdated(data: received)
}
.store(at: cancellables)
//
func handleUpdated(data: String?) {
// 여기서 데이터 접근
print(data)
print(vm.data)
}
이렇게 하면 업데이트 된 값이 무엇인지 직접적으로 알 수 있다. 그치만, HandleUpdated 함수에 매개변수가 생겨서 이게 불편할 수도 있다. 이 함수가 다른 상황에도 쓰이는데, 함수 호출자가 vm에 접근하기 어려운 상황이 있겠다.
@Published 의 속성을 이해해야한다. 이 프로퍼티 래퍼는 값의 변수가 변할 때 알림을 준다. 그래서 뷰가 변화화는데 트리거를 주기 위해 동작한다. 그런데, 어느 시점에 변화가 촉발되는지가 중요하다. 결론부터 말하면 값이 변화가되고 willSet 프로퍼티 감시자에서 .send()를 보낸다. 그렇기 때문에 뷰모델에 접근 했을 때는 아직 값이 업데이트가 되지 않은 것이다. willSet 에서 방출된 이벤트이기 때문에, 뷰모델은 아직 예전 값을 들고 있다.
그렇다면, 왜 didSet이 아니라 willSet에서 값을 업데이트하는가?
SwiftUI에서는 값이 변화할 때마다 뷰를 새로 그리는 것을 컨셉으로 하는데, 성능을 높이기 위해 이전에 값이 무엇인지 알고, 바뀌려는 값이 무엇인지 알아서 값이 새롭게 바뀐 뷰의 영역만 다시 그려야 한다. 그렇기 때문에 이전값(oldValue)를 알 수 있는 willSet에서 .send()를 보내는 것이다.
참조
https://developer.apple.com/documentation/combine
Combine | Apple Developer Documentation
Customize handling of asynchronous events by combining event-processing operators.
developer.apple.com
https://developer.apple.com/documentation/combine/receiving-and-handling-events-with-combine
Receiving and Handling Events with Combine | Apple Developer Documentation
Customize and receive events from asynchronous sources.
developer.apple.com
https://developer.apple.com/documentation/combine/processing-published-elements-with-subscribers
Processing Published Elements with Subscribers | Apple Developer Documentation
Apply back pressure to precisely control when publishers produce elements.
developer.apple.com
Understanding @Published in SwiftUI: Risks and Best Practices
Hello SwiftUI enthusiasts! 🚀 In this article, we’ll demystify the @Published property wrapper, a powerful tool in SwiftUI for updating…
paigeshin1991.medium.com
'SwiftUI - 기초' 카테고리의 다른 글
[SwiftUI-기초] MVVM과 SwiftUI 근데 Combine을 곁들인 (0) | 2025.02.12 |
---|---|
[SwiftUI-기초] NavigationStack 알아보기 (0) | 2025.01.28 |
[SwiftUI-기초] NavigationTitle 수동으로 inline 만들기(with. ToolbarItem) (0) | 2025.01.23 |
[SwiftUI-기초] 프로젝트에서 TabView 두개 사용하기(Multiple TabView) (0) | 2025.01.22 |
[SwiftUI-기초] 수직 확장 되는 텍스트 필드, TextField, TextEditor (0) | 2025.01.20 |