๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
IOS๐ŸŽ/RxSwift

[RxSwift] Error Handling Operator

by Jouureee 2022. 5. 10.

Network Error Handling์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๋‹ค๊ฐ€ ๊ด€๋ จ operator๋“ค์„ ์ •๋ฆฌํ•ด๋ณด๋ ค๊ตฌ ํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ  : https://okanghoon.medium.com/rxswift-5-error-handling-example-9f15176d11fc

 

RxSwift #5โ€Š—โ€ŠError Handling & Example

์ด๋ฒˆ์—๋Š” RxSwift์—์„œ ์–ด๋–ป๊ฒŒ Error Handling ์„ ํ•˜๋Š”์ง€ ์•ฑ์—์„œ ํ”ํžˆ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ ์˜ˆ์ œ๋“ค์„ ํ†ตํ•ด ์•Œ์•„๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

okanghoon.medium.com

 

RxSwift์—์„œ๋Š” Error Handling์„ ํ•˜๋Š”๋ฐ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

 

1. Catch ํŠน์ • ๊ฐ’์œผ๋กœ error ๋ณต๊ตฌ
2. Retry ์žฌ์‹œ๋„
3. materialize / dematerialize : Sequence ๋ฅผ ์ œ์–ดํ•ด์„œ ์ฒ˜๋ฆฌ


1. Catch


Error ๋ฐœ์ƒ ์‹œ, catch ๋‚ด์˜ ํŠน์ • ๊ฐ’์œผ๋กœ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๊ณ  complete ์‹œํ‚จ๋‹ค.


1) 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
    .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)์œผ๋กœ ๋ฐ”๋€Œ์–ด ์ถœ๋ ฅ๋˜๊ณ  complete๋œ๋‹ค.


2) catchErrorJustReturn

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
*/

error๊ฐ€ ๋ฐœ์ƒํ–ˆ์„๋•Œ Error๋ฅผ ๋ฌด์‹œํ•˜๊ณ  ์ง€์ •ํ•œ element๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. 

 

2. retry

source observable์—์„œ error๊ฐ€ ๋ฐœ์ƒ์‹œ ๋‹ค์‹œ subscribeํ•˜์—ฌ error๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๊ธธ ๋ฐ”๋ž€๋‹ค. ๋ผ๊ณ  ์†Œ๊ฐœํ•˜๊ณ  ์žˆ๋‹ค.

 

1) retry()

์—ฐ์‚ฐ์„ ์„ฑ๊ณตํ• ๋•Œ๊นŒ์ง€ ๊ณ„์† ๋ฐ˜๋ณตํ•œ๋‹ค.

let reloadPublisher = PublishSubject<Void>()

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

์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ์„ ๋Š๊ณ  Api๊ฐ€ ์‹คํŒจํ•˜๊ฒŒ ๋˜๋ฉด ๊ณ„์† Api.getRepositories()์„ ํ˜ธ์ถœ ํ•  ๊ฒƒ์ด๋‹ค.

 

2) retry(count)

๋ฐ˜๋ณต ํ˜ธ์ถœํ•  count๋ฅผ ์ง€์ •ํ•œ๋‹ค.

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)")
*/

์žฌ์‹œ๋„ ํšŸ์ˆ˜๋ฅผ ๋„˜์–ด์„œ๋ฉด error๋ฅผ ๋ฐฉ์ถœํ•œ๋‹ค.

 

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
    .retryWhen { 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<Element> Sequence๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค.

Dematerialize

  • Event<Element> 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
*/

 

๋Œ“๊ธ€