ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • RxSwift Error Handling
    RxSwift 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

    댓글

Designed by Tistory.