RxCocoa
Closed this issue · 0 comments
Youngminah commented
bind(to:)
- observable 바인딩
- Producer는 값을 만들어낸다.
- Receiver는 만들어진 값을 수신하고 처리한다.
- observable을 다른 속성에 바인딩하기 위해서는 Receiver가 ObservableType 이어야한다.
- Receiver는 값을 반환할 수 없다. 이 것이 RxSwift에서 사용하는 일반적인 바인딩 규칙이다.
- 요약하자면, bind(to:) 함수는 subscribe()의 특별 맞춤 버전이다.
- 바인더는 Error이벤트를 받지 않는다. (종료되지 않기위해)
- 바인더는 UI Binding에 사용된다.
- 바인더는 메인스레드에서 실행되는것을 보장되어야한다.
바인더 예제
- 위가 텍스트 필드 아래가 레이블
- 텍스트필드에 한글자 쓸 때마다 레이블이 업데이트 된다.
textField.rx.text
.observe(on: MainScheduler.instance) //메인스레드에서 실행 보장 위함
.subscribe(onNext: {
label.text = $0
})
.disposed(by: disposeBag)
- 위의 예제를 바인더를 이용하여 아래의 예제로 재구성
textField.rx.text
.bind(to: label.rx.text)
.disposed(by: disposeBag)
RxCocoa Traits
- RxCocoa는 bindTo 외에도 Cocoa 프레임워크와 UIkit을 다룰 많은 진보된 기능들을 제공
- Trait은 UI와 함께 사용되도록 독점적으로 생성된 observable 항목의 특수한 구현을 제공
- Trait는 직관적이고 작성하기 쉬운 코드를 작성하는데 도움이 되는 Observable의 특수 클래스다. (특히 UI 작업할 때)
참고
: RxSwift의 traits와 마찬가지로 RxCocoa의 traits도 작업에 도움이 되는 특별기능일 뿐 사용은 선택적이다.- Trait를 억지로 사용할 필요는 없다.
- 처음에는 순수히 Subject나 Observable만 쓰는 것도 나쁘지 않다.
- 하지만 만약 컴파일링 중에 또는 UI와 관련된 어떤 예정된 법칙을 체크하고 싶을 때, ❗️
- Trait은 아주 강력한 기능을 제공하며 시간 절약에도 좋다.
- Trait을 사용하면 .observeOn(MainScheduler.instance) 호출에 대해 잊어버려도 좋다.
- 또한 background 쓰레드에서 UI를 생성할 필요도 없다.
- Driver와 ControlProperty가 지금은 어려워 보일 수 있다.
- 천천히 하나씩 확인해보자.
Driver와 Signal
- 에러를 방출하지 않는 특별한 observable
- 모든 과정은 main thread에서 이루어진다. (여기까지는 바인드와 비슷함)
- 부수작용들 공유 가능 (바인드와 다른점) ❗️
- 둘의 차이점은
Driver
는 구독하기 전에 초기값 또는 최신값 replay할 수 있다. Signal
은 구독한 이후에 발생하는 값만 전달- Signal은 emit함수로 이벤트 처리 / Driver는 drive함수로 이벤트 처리
Driver ❗️❗️
- 부수작용 공유 !!
- 구독하기 전에 초기값 또는 최신값 replay할 수 있다.
드라이버 사용전
let result = inputField.rx.text.asDriver()
.flatMapLatest { validateText($0) }
result
.map { $0 ? "Ok" : "Error" }
.bind(to: resultLabel.rx.text)
.disposed(by: bag)
result
.map { $0 ? UIColor.blue : UIColor.red }
.bind(to: resultLabel.rx.backgroundColor)
.disposed(by: bag)
result
.bind(to: sendButton.rx.isEnabled)
.disposed(by: bag)
share를 이용한 뒤 수정 후
let result = inputField.rx.text.asDriver()
.flatMapLatest {
validateText($0)
.observeOn(MainScheduler.instance)
.catchErrorJustReturn(false) // 구독자로 에러전달이 아닌 false이벤트 전달.
}
.share()
result
.map { $0 ? "Ok" : "Error" }
.bind(to: resultLabel.rx.text)
.disposed(by: bag)
result
.map { $0 ? UIColor.blue : UIColor.red }
.bind(to: resultLabel.rx.backgroundColor)
.disposed(by: bag)
result
.bind(to: sendButton.rx.isEnabled)
.disposed(by: bag)
- 드라이버를 사용하면 더 간단하게 바꿀 수 있다.
드라이버 사용후
let result = inputField.rx.text.asDriver()
.flatMapLatest {
validateText($0)
.asDriver(onErrorJustReturn: false)
}
result
.map { $0 ? "Ok" : "Error" }
.drive(resultLabel.rx.text)
.disposed(by: bag)
result
.map { $0 ? UIColor.blue : UIColor.red }
.drive(resultLabel.rx.backgroundColor)
.disposed(by: bag)
result
.drive(sendButton.rx.isEnabled)
.disposed(by: bag)
- 드라이버를 쓰지 않는다면 문제점은 각각의 구독자마다 sequence가 생성.
- 즉 공유하지 않는다.
- 만약 네트워크 통신이라면 심각한 오버헤드가 발생할 것이다.
- 그리고 에러이벤트가 발생했을때, 문제점이있다.
- share를 이용해도 되지만 그럴경우 Main스레드에서 실행하는 코드, 에러핸들링 코드 등등 직접 코드로 구현해주어야한다.
Signal
- 구독한 이후에 발생하는 값만 전달
Rx Extension
extension Reactive where Base: T {}
- 이러한 형태로 쓰인다.
extension Reactive where Base: UIView {
var sizeToFit: Binder<Void> {
return Binder(base) { base, _ in
base.sizeToFit()
}
}
}
Driver.just(Void())
.drive(button.rx.sizeToFit)
.disposed(by: disposeBag)