Driver는 UI에 특화된 Observable로, RxCocoa 프레임워크에 속해있습니다.
RxCocoa는 이전에 Relay에 대해서 알아볼 때도 함께 알아본 적 있습니다.
저는 처음에 UI에 특화된 Observable이라는 게 이해가 잘 되지 않았고, RxSwift에 따로 Observable이 있는데 왜 굳이 Driver가 존재하는지 궁금했습니다.
우선 RxSwift의 Observable과 RxCocoa의 Driver의 가장 큰 차이점을 보자면, Observable은 생성된 Thread의 영향을 받는다는 것입니다.
구분 | Observable | Driver |
---|---|---|
UI 작업 | MainScheduler | MainScheduler |
로직 작업 | BackgroundScheduler | MainScheduler |
RxCocoa의 Driver는 기본적으로 Observable을 wrapping하고, MainScheduler에서만 동작되기 때문에 따로 Scheduler를 처리해줄 필요가 없습니다.
반면, Obervable은 상황에 따라 MainScheduler와 BackgroundScheduler를 지정해줘야합니다.
따라서 UI에 직접적으로 접근해서 처리를 해주어야할 땐 Driver를 사용하는 것이 효율적이고, 로직만 처리를 하는 상황이라면 Observable을 이용해 Background에서 작업을 하는 것이 더 효율적입니다.
흔한 회원가입 뷰에서, 입력 값들을 체크하여 회원가입 버튼의 UI를 업데이트 해주는 작업을 해보도록 하겠습니다.
뷰모델에 입력값들이 형식에 맞는지 체크하고 Bool 값을 반환해주는 함수를 생성해줍니다.
[strcut SignUpViewModel]
private func signUpBtnEnable(nickname: String?, email: Bool?, password: String?, passwordConfirm: String?, agreeState: Bool) -> Bool {
guard let nickname = nickname else { return false }
guard let email = email else { return false }
guard let password = password else { return false }
guard let passwordConfirm = passwordConfirm else { return false }
return nickname.count >= 2 && password.count >= 8 && email && passwordConfirm == password && agreeState
}
위 함수에 여러 입력값들을 combineLatest와 map을 활용해 집어넣고, 반환 값을 관찰하는 변수를 생성하여 output으로 구성해줍니다.
[strcut SignUpViewModel]
struct Output {
var signUpBtnCheck: Driver<Bool>
}
let signUpBtnCheck$ = Observable.combineLatest(nicknameInput$, emailChecked, passwordInput$, passwordConfirmInput$, agreeBtnState).map(signUpBtnEnable).asDriver(onErrorJustReturn: false)
Driver는 Observable과 다르게 error값을 방출하지않기 때문에, onErrorJustReturn을 활용해서 에러가 발생하면 false를 반환하도록 생성해주었습니다.
다시 뷰로 가서, 회원가입 버튼에 뷰모델의 output을 바인딩 받아 사용자의 입력에 따라 버튼의 UI가 자동으로 업데이트 되게 구성하면 됩니다.
[class SignUpViewController]
viewModel.output.signUpBtnCheck
.drive(onNext: { status in
self.signUpBtn.setTitleColor(status ? .white : UIColor.GrayScale.sub4, for: .normal)
self.signUpBtn.backgroundColor = status ? UIColor.Primary.primary : UIColor.GrayScale.gray4
self.signUpBtn.isEnabled = status
})
.disposed(by: disposeBag)