예제 코드)
passwordTextField
에 비밀번호를 입력한 후 비밀번호 확인을 위해
passwordConfirmTextField
에 비밀번호를 입력했을 때 확인 버튼의 활성화, 비활성화를 Combine을 사용해 비동기 처리한 예시입니다.
final class PasswordViewModel {
@Published var passwordInput: String = "" // 비밀번호 입력을 위한 프로퍼티
@Published var passwordConfirmInput: String = "" // 비밀번호 확인 입력을 위한 프로퍼티
lazy var isMatchPasswordInput: AnyPublisher<Bool, Never> = Publishers.CombineLatest($passwordInput, $passwordConfirmInput)
.map { password, passwordConfirm in
if password == "" || passwordConfirm == "" {
return false
}
if password == passwordConfirm {
return true
} else {
return false
}
}
.eraseToAnyPublisher()
}
CombineLatest
는 두 개 이상의 퍼블리셔(Publisher)를 조합하여 새로운 퍼블리셔를 생성하는 역할을 합니다. combineLatest는 각각의 퍼블리셔들이 새로운 이벤트를 방출할 때마다 해당 이벤트들을 조합하여 새로운 값을 방출하는 퍼블리셔를 생성합니다.class PasswordViewController: UIViewController {
@IBOutlet weak var passwordTextField: UITextField!
@IBOutlet weak var passwordConfirmTextField: UITextField!
@IBOutlet weak var confirmBtn: UIButton!
let viewModel = PasswordViewModel()
var subscriptions = Set<AnyCancellable>()
override func viewDidLoad() {
super.viewDidLoad()
configure()
bind()
}
}
private extension PasswordViewController {
func configure() {
navigationItem.title = "비밀번호 매치"
}
func bind() {
passwordTextField.myTextPublisher
.print() // .print()를 통해 들어오는 값을 확인할 수 있습니다.
.receive(on: DispatchQueue.main)
.assign(to: \.passwordInput, on: viewModel)
.store(in: &subscriptions)
passwordConfirmTextField.myTextPublisher
.receive(on: DispatchQueue.main)
.assign(to: \.passwordConfirmInput, on: viewModel)
.store(in: &subscriptions)
viewModel.isMatchPasswordInput
.print()
.receive(on: DispatchQueue.main)
.assign(to: \.isValid, on: confirmBtn)
.store(in: &subscriptions)
}
}
extension UIButton {
var isValid: Bool {
get {
backgroundColor == .systemYellow
}
set {
backgroundColor = newValue ? .systemYellow : .lightGray
isEnabled = newValue
let titleColor: UIColor = newValue ? .systemBlue : .white
setTitleColor(titleColor, for: .normal)
}
}
}
extension UITextField {
var myTextPublisher: AnyPublisher<String, Never> {
NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: self)
.compactMap { $0.object as? UITextField }
.map { $0.text ?? "" }
.eraseToAnyPublisher()
}
}
sink
연산자는 데이터의 특정 처리를 위해 사용 또는 값을 받아와서 클로저에서 원하는 작업을 수행하거나, 값을 변환할 때 사용합니다.assign
을 사용하였습니다.)assign
연산자는 View와 ViewModel 사이에서 데이터를 바인딩 할 때 유용하게 활용합니다. (단순 연결)\.
은 Key Path 문법으로 속성이나 메서드를 가리키는 경로를 나타내는 표현 방식입니다..eraseToAnyPublisher()
는 Publisher 타입을 AnyPublisher
로 변환하는 역할을 합니다.