This is the most elaborate trait. Its intention is to provide
an intuitive way to write reactive code in the UI layer
, or for any case where you want to model a stream of data Driving your application.
error를 방출하지 않습니다.
error가 발생하면 애플리케이션이 사용자의 input에 반응하지 않습니다.(like operating system drivers)
main scheduler에서 observe합니다.
UI element들은 thread safe하지 않음으로 main thread에서 observe되야합니다.
side effect를 공유합니다.
share(replay: 1, scope: .whileConnected)
let results = query.rx.text
.throttle(.milliseconds(300), scheduler: MainScheduler.instance)
.flatMapLatest { query in
fetchAutoCompleteItems(query)
}
results
.map { "\($0.count)" }
.bind(to: resultCount.rx.text)
.disposed(by: disposeBag)
results
.bind(to: resultsTableView.rx.items(cellIdentifier: "Cell")) { (_, result, cell) in
cell.textLabel?.text = "\(result)"
}
.disposed(by: disposeBag)
코드의 intention
코드의 problem
fetchAutoCompleteItems 가 error를 방출하면 모든걸 unbind 하고 UI는 새로운 query에 더 이상 반응하지 않을거다.
/**
Subscribes an element handler to an observable sequence.
In case error occurs in debug mode, `fatalError` will be raised.
In case error occurs in release mode, `error` will be logged.
- parameter onNext: Action to invoke for each element in the observable sequence.
- returns: Subscription object used to unsubscribe from the observable sequence.
*/
public func bind(onNext: @escaping (Element) -> Void) -> Disposable {
self.subscribe(onNext: onNext,
onError: { error in
rxFatalErrorInDebug("Binding error: \(error)")
})
}
fetchAutoCompleteItems 가 background thread에서 results를 return하면 results는 UI element들을 background thread에서 bind하고 non-deterministic crashe가 발생한다.
Results는 2개의 UI element에 bind됐습니다. 각 UI element에 하나씩 HTTP Reqeust가 발생해 총 2번의 Request가 발생한다. 이는 의도된 동작이 아닙니다.
코드 개선
let results = query.rx.text
.throttle(.milliseconds(300), scheduler: MainScheduler.instance)
.flatMapLatest { query in
fetchAutoCompleteItems(query)
.observeOn(MainScheduler.instance) // results are returned on MainScheduler
.catchErrorJustReturn([]) // in the worst case, errors are handled
}
.share(replay: 1) // HTTP requests are shared and results replayed
// to all UI elements
results
.map { "\($0.count)" }
.bind(to: resultCount.rx.text)
.disposed(by: disposeBag)
results
.bind(to: resultsTableView.rx.items(cellIdentifier: "Cell")) { (_, result, cell) in
cell.textLabel?.text = "\(result)"
}
.disposed(by: disposeBag)
driver 사용해서 개선
let results = query.rx.text
.throttle(.milliseconds(300), scheduler: MainScheduler.instance)
.flatMapLatest { query in
fetchAutoCompleteItems(query)
.observeOn(MainScheduler.instance) // results are returned on MainScheduler
.catchErrorJustReturn([]) // in the worst case, errors are handled
}
.share(replay: 1) // HTTP requests are shared and results replayed
// to all UI elements
results
.map { "\($0.count)" }
.bind(to: resultCount.rx.text)
.disposed(by: disposeBag)
results
.bind(to: resultsTableView.rx.items(cellIdentifier: "Cell")) { (_, result, cell) in
cell.textLabel?.text = "\(result)"
}
.disposed(by: disposeBag)