ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • RxSwift에서 combineLatest 와 withLatestFrom 차이 이해하기
    iOS/mvvm + RxSwift 스터디 2019. 3. 3. 17:27

    RxSwift에서 combineLatest 와 withLatestFrom 차이 이해하기

    참고 자료:

    https://medium.com/@martinkonicek/rx-combinelatest-vs-withlatestfrom-ccd98cc1cd41

    http://adamborek.com/combinelatest-withlatestfrom-zip/

    combineLatest

    여러 소스 중에서 단 한 가지라도 이벤트를 방출하면, 각각 소스의 맨 마지막 값을 뽑아서 새로운 값을 방출합니다.

     

    disposables += Observable.combineLatest(
            view.onNameChanged(), view.onEmailChanged(), ::AccountInfo)
            .subscribe { accountInfo ->   
                view.setNextStepButtonEnabled(accountInfo.isValid())
            }
    
    let isPasswordValid = passwordField.rx.text.orEmpty
        .map { $0.characters.count >= 8 }
        .distinctUntilChanged()
        
    let isEmailVaild = emailField.rx.text.orEmpty
        .map(doesEmailMatchRegex)
        .distinctUntilChanged()
        
    let isButtonEnabled = Observable.combineLatest(isPasswordValid, isEmailVaild) { $0 && $1 }    
    
    isButtonEnabled.bind(to: button.rx.isEnabled)
        .disposed(by: disposeBag)
        
    

     

    https://rxmarbles.com/#combineLatest

    • 두 이벤트 모두가 변할 때마다 이벤트를 방출할 때 쓰이기에 적합합니다.

      • 이메일과 비밀번호가 변할 때마다 버튼의 enabled 를 계산할 때

         

    withLatestFrom

    이메일과 비밀번호 검증에 끝났다고 해 봅시다. 이제 버튼을 누르면 로그인 요청을 보내고 싶습니다.

    일단 이렇게 구현할 수 있습니다.

    signInButton.rx.tap
        .flatMap { [weak self] in
            guard let `self` = self else { return .empty() }
            let credential = Credential(email: self.emailField.text, password: self.passwordField.text)
            return self.loginUseCase.login(using: credential)
        }
        .subscribe()
        .disposed(by: disposeBag)
    

    하지만, 이런 방식은 rx스럽지 않고 weak self를 써야 합니다.

    combineLatest 로 버튼 탭, 이메일, 비밀번호 새 옵저블을 합치는 방식을 생각해볼 수 있습니다.

     

    let doLogin = Observable.combineLatest(button.rx.tap, emailField.rx.text.orEmpty, passwordField.rx.text.orEmpty) { ($1, $2) }
    
    doLogin
        .map(Credential.init)
        .flatMap(loginUseCase.login)
        .subscribe()
        .disposed(by: disposeBag)
    
    

    weak self 캡처링을 지웠습니다.

    하지만, 이럴 때 combineLatest 을 쓰면 곤란합니다.

    combineLatest 은 내부 이벤트중 하나라도 발생할 때마다 새로운 이벤트를 발행합니다. 따라서, 이메일이나 비밀번호를 바꿀 때에도 로그인 이벤트가 발생됩니다.

    withLatestFrom 은 한 옵저블을 입력으로 받아서, 다른 옵저버의 맨 마지막 이벤트와 같이 합칩니다.

    let credential = Observable.combineLatest(emailField.rx.text.orEmpty, passwordField.rx.text.orEmpty, resultSelector: Credential.init)
    
    button.rx.tap
        .withLatestFrom(credential)
        .flatMap(loginUseCase.login)
        .subscribe()
        .disposed(by: disposeBag)
    

     

     

    로그인

    사용자가 이름을 입력하는 뷰를 제작하고 이름을 검증할 때, 이렇게 사용할 수 있습니다:

    disposables += view.onNameFocusChanged()      // When focus changes
              .withLatestFrom(view.onNameChanged()) // Grab the name
              .subscribe { (focus, name) ->
                val hasLostFocus = !focus
                if (hasLostFocus && !isValidName(name)) {
                  view.showInvalidName()
                } else {
                  view.clearNameError()
                }
              }
    

     

    • 포커스가 변할 때만 이벤트를 방출합니다.
    • 사용자가 이름을 입력하지 않고 포커스만 주면, 이벤트를 방출하지 않습니다.
    • 포커스를 주지 않고 이름만 어떻게 바뀌어도, 이벤트를 방출하지 않습니다.

    https://rxmarbles.com/#withLatestFrom

     

    요약

    • combineLatestisEnabled 같은 상태 변수를 계산할 때 적합합니다.
    • withLatestFrom 은 다른 액션을 발동시킬 때 유용합니다.
Designed by Tistory.