디버깅
doOnEach()
- RxSwift에서는 do() 함수 내에 각 상황에 대한 클로저로 정의되어 있다.
let observable = Observable.from([1,2,3])
observable.do { value in
print(value) // 값 수신과 동시에 실행
} afterNext: { value in
print(value) // 값 수신 직후 실행
} onError: { error in
print(error) // 에러가 나타날 때 실행
} afterError: { error in
print(error) // 에러로 인해 스트림이 종료된 후 실행
} onCompleted: {
print("complete") // complete 종료와 함께 실행
} afterCompleted: {
print("afterComplete") // complete 직후 실행
} onSubscribe: {
print("subscribe") // subscribe 이전 실행
} onSubscribed: {
print("subscribed") // subscribe 직후 실행
} onDispose: {
print("disposed") // 어떠한 이유로든 옵저버블이 dispose되고 나서 실행
}
예외 처리
RxJava와의 차이점
- RxSwift에서 Observable 자체에 구현된 예외처리 메소드는 없다.
- 예외 처리는 실패하지 않는 옵저버블인 Driver, Single등에 구현되어 있으며 asDriver()등의 메소드를 통해 예외 처리와 함께 변환할 수 있다.
- Driver, Single은 Main Thread에서만 실행된다.
onErrorReturn()
// 1/4 확률로 에러를 던진다
func troubleMaker(val: Int) throws -> Int {
let toss = Bool.random() && Bool.random()
if toss { throw NSError() }
return val
}
- 에러 처리 시 무조건 지정한 값을 발행하고 종료된다.
Observable.from([1,2,3,4,5])
.map { value in
return try self.troubleMaker(val: value)
}.do(onError: { _ in print("!!") })
.asDriver(onErrorJustReturn: -1)
.drive { result in print(result) }.disposed(by: self.disposeBag)
// 1 2 3 !! -1
onErrorDriveWith()
- 에러가 난 경우 다른 Driver로 대체하여 구독한다. Driver.empty()등을 사용해 안전하게 예외 처리가 가능하다.
let recovery = Observable.from([6,7,8,9,10]).asDriver(onErrorJustReturn: 0)
Observable.from([1,2,3,4,5])
.map { value in
return try self.troubleMaker(val: value)
}.do(onError: { _ in print("!!") })
.asDriver(onErrorDriveWith: self.recovery)
.drive { result in print(result) }.disposed(by: self.disposeBag)
// 1 2 3 !! 6 7 8 9 10
retry()
- 에러 시 반복 회수를 지정할 수 있으며, 인자 없이 사용하면 무제한 반복한다.
Observable.from([1,2,3,4,5,6,7,8,9,10])
.map { elem in
return try self.troubleMaker(val: elem)
}.do(onError: { _ in print("!!") })
.retry(3) // 3번까지 재시도
.bind { print($0) }
.disposed(by: self.disposeBag)
// 1 2 3 !! 1 2 !! 1 2 3 4 !! 1 !!
retry(when:)
- retryHandler를 주면 retryHandler가 이벤트를 발행할 때마다 구독을 재시도한다.
let retryHandler = Observable<Int>.interval(.seconds(3), scheduler: MainScheduler.instance)
Observable.from([1,2,3,4,5,6,7,8,9,10])
.map { elem in
return try self.troubleMaker(val: elem)
}.do(onError: { _ in print("!!") })
.retry(when: { _ in return retryHandler})
.bind { print ($0) }
.disposed(by: self.disposeBag)
// 1 !! 1 2 !! ...
흐름 제어
sample()
- RxSwift에서는 시간과 관련 없이 두 번째 옵저버블이 이벤트를 발행할 때 첫 번째 옵저버블의 최근 데이터를 발행한다.
let tap = PublishSubject<Void>()
let typingText = PublishSubject<String>()
typingText.sample(tap)
.bind { value in
print(value)
}.disposed(by: self.disposeBag)
typingText.onNext("ASD") // Nothing printed
tap.onNext(()) // ASD
buffer()
- 지정된 개수를 모두 채우거나, 시간이 끝난 경우 배열로 묶어서 내려보낸다.
- 시간에 0초를 주면 arraychunk처럼 사용 가능
let observable = Observable.from([1,2,3,4,5,6,7,8,9,10])
observable.buffer(timeSpan: .seconds(1), count: 3, scheduler: MainScheduler.instance)
.bind { arr in
print(arr)
}
.disposed(by: self.disposeBag)
// [1,2,3,] [4,5,6,] [7,8,9,] (1SEC..) [10]
window()
- buffer()와 유사하지만, 배열이 아닌 Cold Observable의 형태로 내려보내준다.
- map()과 flatMap()의 관계
let observable = Observable.from([1,2,3,4,5,6,7,8,9,10])
observable.window(timeSpan: .seconds(1), count: 3, scheduler: MainScheduler.instance)
.bind { ob in
ob.bind { value in
print(value)
}.disposed(by: self.disposeBag)
}.disposed(by: self.disposeBag)
// 1,2,3,4,5,6,7,8,9,10
throttleFirst(), throttleLast()
- 해당 시간동안 새로운 이벤트가 전달되는 것을 막는다.
- RxSwift에서는 latest flag로 결정되며 기본값은 true이다(throttleLast 동작)
let safeButton = UIButton()
// throttleFirst
safeButton.rx.tap
.throttle(.milliseconds(600), latest: false, scheduler: MainScheduler.instance)
.bind { _ in self.moveNext() } // 처음 이벤트 전달
.disposed(by: disposeBag)
// throttleLast
safeButton.rx.tap
.throttle(.milliseconds(600), latest: true, scheduler: MainScheduler.instance)
.bind { _ in self.moveNext() } // 나중 이벤트 전달
.disposed(by: disposeBag)
debounce()
- throttle과 비슷하나 새 이벤트가 내려올 때마다 시간이 연장된다.
let searchText = PublishSubject<String>()
searchText.debounce(.milliseconds(600), scheduler: MainScheduler.instance)
.flatMap { self.networking() }
.bind { text in print(text) } // 나중 이벤트 전달
.disposed(by: disposeBag)