RxSwift - Error Handling Operators

JSLee·2022년 3월 14일
0
post-thumbnail

Error Handling

RxSwift 를 사용하면서 흔히 발생하는 Error 를 어떤 Operator 를 사용하여 Handling 하는지에 대하여 알아 보려고 합니다.

Catch

1. catch : 특정 값으로 Error 복구

  • Swift 에서 사용하던 do / try / catch 문과 유사하다
  • 에러가 발생했을 때 Error 이벤트로 종료되지 않게 한다.
  • Error 이벤트 대신 특정 값의 이벤트를 발생시키고 complete 시킨다.

1) catchError(_ handler: (Swift.Error)-> Observable)

  • Error를 다른 타입의 Observable 로 반환하는 클로저를 parameter로 받는다.
  • Error가 발생했을 때 Error를 무시하고 클로저의 반환값(Observable)을 반환한다.
let observable = Observable<Int>
    .create { observer -> Disposable in
        observer.onNext(1)
        observer.onNext(2)
        observer.onNext(3)
        observer.onError(NSError(domain: "", code: 100, userInfo: nil))
        observer.onError(NSError(domain: "", code: 200, userInfo: nil))
        return Disposables.create { }
}

observable
    .catchError { .just(($0 as NSError).code) }
    .subscribe { print($0) }
    .disposed(by: disposeBag)

/* Prints:
    next(1)
    next(2)
    next(3)
    next(100)
    completed
*/
  • 1, 2, 3 이 정상적으로 출력되는걸 확인할 수 있다.

  • code: 100 Error가 발생하고 catchError에서 Error 가 .just(100)로 바뀌어 에러코드가 print 되고 complete 된걸 확인할 수 있습니다.

2) catchErrorJustReturn(_ element: E)

  • Error 가 발생했을 때 Error 를 무시하고 element를 반환한다.
  • 모든 에러에 동일한 값이 반환되기 때문에 catchError 에 비해 제한적이다.
let observable = Observable<Int>
    .create { observer -> Disposable in
        observer.onNext(1)
        observer.onNext(2)
        observer.onNext(3)
        observer.onError(NSError(domain: "", code: 100, userInfo: nil))
        observer.onError(NSError(domain: "", code: 200, userInfo: nil))
        return Disposables.create { }
}
observable
    .catchErrorJustReturn(999)
    .subscribe { print($0) }
    .disposed(by: disposeBag)

/* Prints:
    next(1)
    next(2)
    next(3)
    next(999)
    completed
*/
  
  
  • 1, 2, 3 이 정상적으로 print 되는걸 확인할 수 있다.
  • code: 100 Error가 catchErrorJustReturn 에서 999 로 바뀌어 print 되고 complete 되는걸 확인할 수 있다.

2. retry

  • 말 그대로 에러가 발생 했을 때 다시 시도할 수 있게 해준다.

  • 에러가 발생했을 때 Observable 을 다시 시도한다(Observable 내의 전체작업을 반복)

  • 아래의 사진처럼 네트워킹 요청에서 에러가 발생할 경우 retry 를 사용해 다시 시도할 수 있다.

    1) retry()

  • 일반적인 재시도에 사용하는 연산자이다. 에러가 발생했을 때 성공할 때까지 Observable을 다시 시도한다.

  • 아래의 코드는 네트워킹을 통해 repositories를 가져오는 코드이다. 인터넷 연결을 끊고 reload를 하면 콘솔에 실패 메세지가 계속 찍히는 것을 확인할 수 있다.

let reloadPublisher = PublishSubject<Void>()

reloadPublisher
  .flatMap {
    Api.getRepositories()
      .retry()
  }

2) retry(_ maxAttemptCount: Int)

  • 몇 번에 걸쳐서 재시도 할지 지정할 수 있는 연산자이다.
  • maxAttemptCount 가 3 이라면 총 3번의 요청을 보낸다. (재시도는 2번)
  • 재시도 횟수가 넘어가면 그대로 Error를 이벤트로 전달한다.
let reloadPublisher = PublishSubject<Void>()

reloadPublisher
  .flatMap {
    Api.getRepositories()
      .retry(3)
  }
let observable = Observable<Int>
    .create { observer -> Disposable in
        observer.onNext(1)
        observer.onNext(2)
        observer.onNext(3)
        observer.onError(NSError(domain: "", code: 100, userInfo: nil))
        observer.onError(NSError(domain: "", code: 200, userInfo: nil))
        return Disposables.create { }
}

observable
    .retry(2)
    .subscribe { print($0) }
    .disposed(by: disposeBag)

/* Prints:
  next(1)
  next(2)
  next(3)
  next(1)
  next(2)
  next(3)
  error(Error Domain= Code=100 "(null)")
*/

3) retryWhen

  • 재시도 하는 시점을 지정할 수 있고, 한번만 수행한다.
  • retry 와 다르게 마지막 Error를 이벤트로 전달하지 않는다.
let observable = Observable<Int>
    .create { observer -> Disposable in
        observer.onNext(1)
        observer.onNext(2)
        observer.onNext(3)
        observer.onError(NSError(domain: "", code: 100, userInfo: nil))
        observer.onError(NSError(domain: "", code: 200, userInfo: nil))
        return Disposables.create { }
}

observable
    .retry { err -> Observable<Int> in
        return .timer(3, scheduler: MainScheduler.instance)
    }
    .subscribe { print($0) }
    .disposed(by: disposeBag)

/* Prints:
    next(1)
    next(2)
    next(3)
    (3 seconds)
    next(1)
    next(2)
    next(3)
    completed
*/

3. Materialize / Dematerialize

  • RxSwift 에서 쓰이는 Event Sequence를 제어할 수 있게 해준다.
  • 보통 Materialize / Dematerialize 는 함께 사용한다.
  • Observable을 분해할 수 있기 때문에 신중하게 사용해야 한다.

Materialize

Sequence 를 Event Sequence로 변환한다.

Dematerialize

  • Event Sequence 를 다시 Sequence로 변환한다.

  let observable = Observable<Int>
    .create { observer -> Disposable in
        observer.onNext(1)
        observer.onNext(2)
        observer.onNext(3)
        observer.onError(NSError(domain: "", code: 100, userInfo: nil))
        observer.onError(NSError(domain: "", code: 200, userInfo: nil))
        return Disposables.create { }
}

observable
    .materialize()
    .map { event -> Event<Int> in
        switch event {
        case .error:
            return .next(999)
        default:
            return event
        }
    }
    .dematerialize()
    .subscribe { event in
        print(event)
    }
    .disposed(by: disposeBag)

/* Prints
  next(1)
  next(2)
  next(3)
  next(999)
  completed
*/
  • 위의 예제에서는 materialize 를 사용해 event로 변환한 후 error 일 경우 next 로 교체해주었다.
profile
iOS/Android/FE/BE

0개의 댓글