버그 잡이

RxSwift - 버튼 활성화 상태 제어하기 #CombineLatest #Binding 본문

RxSwift

RxSwift - 버튼 활성화 상태 제어하기 #CombineLatest #Binding

버그잡이 2020. 9. 16. 22:50

RxSwift 를 활용하면 버튼의 활성화 상태를 보다 쉽게 제어할 수 있습니다.

 

이를 위해서는 먼저 RxSwift의 두 가지 Operator에 대해서 이해할 필요가 있습니다.

 

 

 

Combine Latest

 

2개 이상의 Observable을 합쳐서 발행해줍니다.

근데 그 패턴이 좀 독특해서 그림으로 이해할 필요가 있습니다.

 

 

 

combineLatest는 다음과 같은 특징이 있습니다.

 

1. 합쳐지는 observable 들이 각각 최초 발행해야 result가 생성된다.

2. 둘 다 onCompleted 되어야 onComplete된다.

3. error는 하나만 발생해도 error를 전달한다.

 

 

(코드로 살펴보겠습니다.)

let bag = DisposeBag()

enum MyError: Error {
   case error
}

let greetings = PublishSubject<String>()
let languages = PublishSubject<String>()

Observable.combineLatest(greetings, languages){ first, second
    -> String in
    return "\(first) \(second)"
}
    .subscribe{ print($0) }
    .disposed(by: bag)

greetings.onNext("hi") // nothing
languages.onNext("world") // hi world

greetings.onNext("hello") // hello world
languages.onError(MyError.error) // error

 

 

 

 

Binding

 

binding은 rxcocoa에서 지원하는 기능으로 UI-Binding을 가능하게 해줍니다.

즉, 데이터와 View를 binding해서 데이터가 변할때마다 이를 View에 반영해줄 수 있습니다,

 

이는 UI와 관련이 있기 때문에 main-thread에서 실행되고 error 이벤트를 받지 않습니다.

(error 이벤트를 받게 되면 앱이 종료되겠죠?)

 

 

기존에 RxSwfit를 활용해서 textField의 input을 view에 바로 반영하려면 아래와 같이 코드를 작성 해야합니다.

valueField.rx.text
  .subscribe(onNext: {[weak self] str in
		self?.valueLabel.text = str})

그리고 위의 코드는 완벽하지 않습니다.

왜냐하면 View의 최신화는 main-thread에서 해줘야합니다.

따라서 DispatchQueue로 감싸주거나 observeOn(MainScheduler.instance)로 스레드를 설정 해줘야 합니다.

 

 

바인딩을 활용하면 보다 간결하게 코드를 작성할 수 있습니다.

valueField.rx.text
	.bind(to: valueLabel.rx.text)
	.disposed(by: disposeBag)

 

 

 

 

버튼 활성화 여부 제어

 

오늘의 하이라이트 RxSwift를 활용한 버튼 활성화 여부 변경하기 입니다.

로그인 화면에서 아이디와 비밀번호 field에 공백에 없어야 "로그인 버튼" 을 활성화 시켜야 하는 경우가 있습니다.

RxSwift를 활용하면 보다 깔끔하고 직관적으로 이를 처리할 수 있습니다.

 

var a = BehaviorSubject<Bool>()
var b = BehaviorSubject<Bool>()

idField.rx.text
  .subscribe(onNext: { 
  	if $0 == "" {
    	a.onNext(false)
    } else {
    	a.onNext(true)
    }
  })
  
pwdField.rx.text
  .subscribe(onNext: { 
  	if $0 == "" {
    	b.onNext(false)
    } else {
    	b.onNext(true)
    }
  })

Observable.combineLatest(a,b) {$0 && $1}
	.bind(to: loginButton.rx.isEnabled)
	.disposed(by: disposeBag)

 

위 코드에서

a는 아이디 입력 textField가 공백인지 check하는 observable

b는 비밀번호 입력 textField가 공백인지 check하는 observable

입니다.

 

이 둘의 상태를 combineLatest로 묶어줌으로써 하나의 상태라도 변경될 때마다 observable이 발행되고 

loginButton.rx.isEnabled = a && b 로 최신화 되는 것입니다.

 

 

반응형
Comments