기어가더라도 제대로

[SwiftUI-기초] @Published 프로퍼티의 데이터 동기화 하는 방법 본문

SwiftUI - 기초

[SwiftUI-기초] @Published 프로퍼티의 데이터 동기화 하는 방법

Damagucci-juice 2025. 1. 24. 08:00

상황 설명

뷰 컨트롤러에서 뷰 모델의 스트림을 구독을 하고 뷰에서 이벤트를 트리거했을 때, 이벤트 스트림 내부에서 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

https://paigeshin1991.medium.com/understanding-published-in-swiftui-risks-and-best-practices-e4a799981c35

 

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

Comments