관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] RxSwift, 응답 재시도 처리 retryWhen, retry(when:) 사용 방법 본문

RxSwift/RxSwift 응용

[iOS - swift] RxSwift, 응답 재시도 처리 retryWhen, retry(when:) 사용 방법

jake-kim 2022. 2. 15. 23:31

재시도 처리

  • retryWhen 사용하여 아래와 같은 상황에서 재시도 처리
    • Observable로 감쌓여진 API 호출로부터 error 응답을 받은 경우, 특정 시간 이후에 다시 재시도 방법?
    • Observable에 감쌓여진 특정 로직 처리 후 error 이벤트가 발생한 경우, 특정 시간 이후 다시 재시도 방법?

사용한 프레임워크

retryWhen

  • closure를 파라미터로 받아서 해당 해당 closure가 완료되면 시도했던 로직 self로 다시 sequence로 만들어서 동작
//  PrimitiveSequence.swift

public func retryWhen<TriggerObservable: ObservableType>(
  _ notificationHandler: @escaping (Observable<Swift.Error>) -> TriggerObservable
)  -> PrimitiveSequence<Trait, Element> { 
  return PrimitiveSequence(raw: self.source.retryWhen(notificationHandler))
}
//  RetryWhen.swift

public func retryWhen<TriggerObservable: ObservableType>(
  _ notificationHandler: @escaping (Observable<Swift.Error>) -> TriggerObservable
)  -> Observable<Element> {
  return RetryWhenSequence(sources: InfiniteSequence(repeatedValue: self.asObservable()), notificationHandler: notificationHandler)
}

retryWhen 사용 방법

  • 예제 API - 사용하는 쪽에서 error 처리 테스트를 위해서 error를 방출하는 API 정의
    var count = 0
    enum API {
      static func getName() -> Observable<String> {
        Observable<String>.create { observer in
          observer.onNext("sample name")
          
          // 오류 테스트를 위한 코드
          observer.onError(SomeError.sample)
          
          return Disposables.create {
            count += 1
            print("dispose getName \(count)")
          }
        }
      }
    }​
  • retry(when:) 을 subscribe()하기 전에 사용하고, retry(when:)의 클로저 내부에서 error.flatMap {}을 사용
    • Observable<Int>.timer를 통해서 언제 재시도를 할건지 정의
    • retry(when:) 다음 스트림에 take(3)을 넣어서 얼마만큼 반복할건지 명시
      API.getName()
        .retry(when: { error in
          error.flatMap { _ in
            Observable<Int>
              .timer(.seconds(3), period: nil, scheduler: MainScheduler.asyncInstance)
          }
        })
        .take(3)
        .subscribe()
        .disposed(by: self.disposeBag)​

결과

* 전체 코드

// ViewController.swift

import UIKit
import RxSwift
import RxCocoa

enum SomeError: Error {
  case sample
}

class ViewController: UIViewController {
  private let disposeBag = DisposeBag()
  
  override func viewDidLoad() {
    super.viewDidLoad()
    
    API.getName()
      .retry(when: { error in
        error.flatMap { _ in
          Observable<Int>
            .timer(.seconds(3), period: nil, scheduler: MainScheduler.asyncInstance)
        }
      })
      .take(3)
      .subscribe()
      .disposed(by: self.disposeBag)
  }
}

var count = 0
enum API {
  static func getName() -> Observable<String> {
    Observable<String>.create { observer in
      observer.onNext("sample name")
      
      // 오류 테스트를 위한 코드
      observer.onError(SomeError.sample)
      
      return Disposables.create {
        count += 1
        print("dispose getName \(count)")
      }
    }
  }
}

 

Comments