-
RxSwift Error HandlingRxSwift 2021. 8. 10. 16:54
보통 에러가 전달되면 그 뒤 next가 전달되지 않음. (구독이 종료되기때문)
그러면
네트워크 에러 -> UI Update (Next Completed) 가 전달되지 않는 문제가 생길 수 있다.
이를 위해서 두가지로 해결 가능하다.
1. catchError 를 통해 새로운 Observable를 리턴하기
2. retry를 통해 Error를 재시도 (무한정/ 갯수 제한) -> Observable
catchError Operator
// 네트워크 요청을 구현할때 자주 사용 // 올바른 응답을 받지 못했을때 local cache를 사용하거나 등등으로 활용 가능 let bag = DisposeBag() let MyError: Error { case error } let subject = PublisthSubject<Int>() let recovery = PublisthSubject<Int>() subject .catchError { _ in recovery } .subscribe { print($0) } .dispose(by: bag) subject.onError(MyError.error) // 구독자에게 error 연산자가 전달되지 않음. subject.onNext(11) // 이렇게 해도 다음을 전달할 수 없음 recovery.onNext(22) // 이렇게 하면 방출 가능 // 즉, catchError는 에러가 났을때 새로운 Observable로 교체한다고 보면 된다. // .catchErrorJustReturn 도 있음. 파라미터로 전달한 기본값을 전달함. 편하게 .catchErrorJustReturn(-1) 이런식으로 씀
retry Operator
// 에러가 났을때 구독을 해제하고 다시 새로운 구독을 시작함. let bag = DisposeBag() let MyError: Error { case error } var attempts = 1 let sorce = Observable<Int>().create { observer in let currentAttempts = attempts if attempts < 3 { observer.onError(MyError.error) attempts += 1 } observer.onNext(1) observer.onNext(2) observer.onCompleted() return Disposable.create{ print(\(currentAttempts)) } source .retry(7) // 이런식으로 최대를 지정해두는게 좋다. 실제로 재시도는 6번 하는거임 .subscribe { print($0) } .dispose(by: bag)
그런데 만약, retry를 버튼을 눌렀을때만 하고 싶다면? 아래 retryWhen을 사용하면 된다!
retryWhen
// 에러가 났을때 구독을 해제하고 다시 새로운 구독을 시작함. let bag = DisposeBag() let MyError: Error { case error } var attempts = 1 let sorce = Observable<Int>().create { observer in let currentAttempts = attempts if attempts < 3 { observer.onError(MyError.error) attempts += 1 } observer.onNext(1) observer.onNext(2) observer.onCompleted() return Disposable.create{ print(\(currentAttempts)) } let trigger = PublishSubject<Void>() source .retryWhen { _ in trigger} .subscribe { print($0) } .dispose(by: bag) trigger.onNext(()) // 요렇게~~ 해야 시도함 // retryWhen은 closure를 파라미터로 받음.
++ Timeout
이벤트가 일정시간동안 발생하지 않으면 오류를 발생시킨다.
let timer = Observable<Int>.create { observer in let timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global()) timer.schedule(deadline: DispatchTime.now() + 1, repeating: 1) let cancel = Disposables.create { timer.cancel() } var next = 0 timer.setEventHandler { if cancel.isDisposed { return } if next < 3 { observer.on(.next(next)) next += 1 } } timer.resume() return cancel } timer.debug().timeout(2, scheduler: MainScheduler.instance).subscribe().disposed(by: disposeBag)let timer = Observable<Int>.create { observer in let timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global()) timer.schedule(deadline: DispatchTime.now() + 1, repeating: 1) let cancel = Disposables.create { timer.cancel() } var next = 0 timer.setEventHandler { if cancel.isDisposed { return } if next < 3 { observer.on(.next(next)) next += 1 } } timer.resume() return cancel } // 이부분이 하이라이트 timer.debug().timeout(2, scheduler: MainScheduler.instance).subscribe().disposed(by: disposeBag)
타임아웃 예제 출처 : https://brunch.co.kr/@tilltue/8
728x90'RxSwift' 카테고리의 다른 글
RxSwift 를 사용하는 이유, 선언적 프로그래밍에 대해 (+선언적 UI) (0) 2021.12.16 Combine vs RxSwift (0) 2021.12.13 amb Operator (0) 2021.08.10 Scheduler (0) 2021.08.10 Combining Operators (0) 2021.08.06