https://explorer89.tistory.com/82
ObservableObject와 @Published
개체가 변경되기 전에 내보내는 게시자가 있는 개체 형식이다.? @Published와 ObservableObject는 스위프트의 Combine 프레임워크에서 사용되는 속성 래퍼와 프로토콜이다. ✅ 주로 SwiftUI와 함꼐 사용
explorer89.tistory.com
ObservableObject와 @Published
ObservableObject
- ObservableObject는 Combine 프레임워크에서 제공하는 프로토콜로, 객체의 상태가 변경될 때 이를 외부에 알리는 역할을 합니다.
- UIKit에서는 ObservableObject를 사용하더라도 SwiftUI와 같은 UI 자동 갱신은 없으므로, 변경된 데이터를 반영하려면 수동으로 UI를 업데이트하는 코드가 필요합니다.
@Published
- @Published는 ObservableObject와 함께 사용되는 속성 래퍼로, 해당 프로퍼티의 값이 변경되면 Combine을 통해 변경 사항이 외부로 알림됩니다.
- UIKit 환경에서는 @Published로 알림을 받고, 이를 통해 데이터 변경 사항에 따라 UI를 갱신할 수 있습니다.
final class AuthenticationViewViewModel: ObservableObject {
@Published var email: String?
@Published var password: String?
@Published var isAuthenticationFormValid: Bool = false
...
}
🔴 ObservableObject와 @Published의 역할 차이
ObservableObject
- 클래스 단위로 변경 사항을 외부에 알리는 역할을 합니다.
- 해당 클래스의 프로퍼티 중에서 변경 감지가 필요한 것을 @Published로 선언해야만 작동합니다. 즉, ObservableObject는 전체 클래스가 변경 사항을 외부로 알릴 준비가 되어 있음을 나타내는 프로토콜일 뿐입니다.
@Published
- ObservableObject와 함께 사용되며, 특정 프로퍼티의 변경을 감지하고 외부로 알립니다.
- 프로퍼티 단위로 작동하며, ObservableObject 없이 단독으로 사용할 수는 없습니다.
둘 다 필요한 이유
- ObservableObject는 클래스 자체가 변경 사항을 알릴 수 있는 객체임을 정의합니다.
- @Published는 어떤 프로퍼티가 변경 사항을 감지할 대상인지를 명시합니다.
- 모든 프로퍼티가 변경 사항을 알릴 필요는 없기 때문에, 변경 감지가 필요한 프로퍼티만 @Published로 선언합니다.
final class ViewModel: ObservableObject {
@Published var name: String = "" // 외부에서 변경 사항 감지
var nonPublishedProperty: String = "" // 변경 사항 감지 안 함
}
private var subscription: Set<AnyCancellable> = []
AnyCancellable
- Combine 프레임워크에서 제공하는 객체로, 비동기 스트림의 구독을 관리합니다.
- 특정 구독이 필요 없어질 때 이를 해제하려면 AnyCancellable의 인스턴스를 취소(cancellation)해야 합니다.
Set<AnyCancellable>
- 여러 AnyCancellable 객체를 저장하는 컬렉션으로, Combine 구독을 일괄 관리합니다.
- 뷰 모델이 메모리에서 해제되면, Set<AnyCancellable>도 함께 소멸되며 모든 구독이 자동으로 취소됩니다.
AuthManager.shared.registerUser(with: email, password: password)
.sink { completion in
switch completion {
case .finished:
print("User registration completed.")
case .failure(let error):
print("Failed to register user: \(error)")
}
} receiveValue: { user in
print("Registered user: \(user)")
}
.store(in: &subscription)
🔴 Set<AnyCancellable>에 대한 이해
역할
- Combine에서 생성된 모든 구독(sink, map, flatMap 등)을 저장하고 관리합니다.
- 구독을 저장하지 않으면 해당 구독이 생성되자마자 해제됩니다. 따라서 Set<AnyCancellable>을 사용해 구독의 수명을 명시적으로 관리합니다.
Combine을 사용하는 모든 경우에 필요한가?
- 꼭 Set<AnyCancellable>이 필요한 것은 아닙니다.
- SwiftUI에서는 구독을 onAppear 등에서 생성하고 SwiftUI가 뷰의 생명주기를 자동으로 관리해 주기 때문에, 명시적으로 Set<AnyCancellable>을 관리하지 않아도 되는 경우가 있습니다.
- 하지만 UIKit에서는 뷰 컨트롤러나 뷰 모델이 Combine 구독을 수동으로 관리해야 하므로 Set<AnyCancellable>이 거의 필수적으로 사용됩니다.
UIKit에서의 데이터 상태 관리
- @Published를 활용한 데이터 상태 관리: UIKit에서는 Combine의 sink 또는 assign 메서드를 사용하여 뷰 모델의 @Published 프로퍼티 변경 사항을 감지하고, UI 업데이트를 직접 처리해야 합니다.
- 예를 들어, isRegistrationFormValid가 변경될 때 버튼 활성화 상태를 업데이트하려면 다음과 같은 방식으로 구현합니다.
viewModel.$isRegistrationFormValid
.sink { [weak self] isValid in
self?.registerButton.isEnabled = isValid
}
.store(in: &subscriptions)
실제로 사용한 코드
private func bindViews() {
emailTextField.addTarget(self, action: #selector(didChangeEmailField), for: .editingChanged)
passwordTextField.addTarget(self, action: #selector(didChangePasswordField), for: .editingChanged)
viewModel.$isAuthenticationFormValid.sink { [weak self] validationState in
self?.registerButton.isEnabled = validationState
}
.store(in: &subscription)
viewModel.$user.sink { [weak self] user in
// 계정 생성, 로그인이 완료 되면 등록창 끄기
guard user != nil else { return }
//self?.dismiss(animated: true)
guard let onBoardingVC = self?.navigationController?.viewControllers.first as? OnboardingViewController else { return }
onBoardingVC.dismiss(animated: true)
}
.store(in: &subscription)
viewModel.$error.sink { [weak self] errorString in
guard let error = errorString else { return }
self?.presentAlert(with: error)
}
.store(in: &subscription)
}
'Swift' 카테고리의 다른 글
Facebook 로그인 구현 + Firebase 연결 (0) | 2025.01.20 |
---|---|
setCustomSpacing(_:after:)의 역할 (0) | 2025.01.05 |
UICollectionViewCompositionalLayout 관련 데이터 소스 관리 (0) | 2025.01.03 |
async / await 사용해보기 (0) | 2025.01.02 |
async/await란? (0) | 2024.12.18 |