관리 메뉴

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

[iOS - swift] RxSwift로 retry 사용 방법 (네트워크 재시도, 에러처리) 본문

iOS 응용 (swift)

[iOS - swift] RxSwift로 retry 사용 방법 (네트워크 재시도, 에러처리)

jake-kim 2022. 6. 18. 14:31

retry를 쓰는 케이스

  • 특정 네트워크에서 request 후에 response로 error를 받은 경우, 횟수나 exponential같은 시간 정책을 정해서 retry하는 경우에 사용
  • 특정 처리에서 error를 받은 경우, 몇초 있다가 몇번은 retry하는 경우 사용
  • RxSwift를 사용한다고 할때, Observable 스트림에서 retry 연산자를 통해 쉽게 처리가 가능

예제에 사용할 프레임워크

데이터 준비

  • 3개의 데이터를 방출한 후 error를 방출하는 Observable 생성
let someObservable = Observable<String>.create { observer in
  observer.onNext("1")
  observer.onNext("2")
  observer.onNext("3")
  let myError = NSError(domain: "MyError", code: 0)
  observer.onError(myError)
  return Disposables.create()
}
  • 데이터 확인
self.someObservable
  .subscribe { print($0) }
  .disposed(by: self.disposeBag)
  
/*
 next(1)
 next(2)
 next(3)
 error(Error Domain=MyError Code=0 "(null)")
 */

연산자 retry(_:)

  • 단순히 몇번 재시도할 것인지 확인
// RxSwift.swift

public func retry(_ maxAttemptCount: Int)
  -> Observable<Element> {
  CatchSequence(sources: Swift.repeatElement(self.asObservable(), count: maxAttemptCount))
}
  • 사용방법 - 인자에 몇번 시도할것인지 값을 넣어서 사용
    • 주의) 2를 넣으면 2번 시도한다는 의미 (2번재시도가 아님을 주의)
self.someObservable
  .retry(2) // 에러가 발생한 경우 한 번 retry
  .subscribe { print($0) }
  .disposed(by: self.disposeBag)
  
/*
 next(1)
 next(2)
 next(3)
 next(1)
 next(2)
 next(3)
 error(Error Domain=MyError Code=0 "(null)")
 */

연산자 retry(when:)

  • error Observable을 그대로 전달받고, 리턴타입으로는 delay와 같은 TriggerObservable을 리턴받는 타입)
public func retry<TriggerObservable: ObservableType>(
  when notificationHandler: @escaping (Observable<Swift.Error>) -> TriggerObservable
) -> Observable<Element> {
  RetryWhenSequence(sources: InfiniteSequence(repeatedValue: self.asObservable()), notificationHandler: notificationHandler)
}
  • 구현
    • 횟수: errorObservable.enumerated()을 하면 몇번 시도하는지 count값을 알 수 있으므로 이 값을 사용
    • 딜레이: Observable의 delay연산자를 통해 몇초후에 시도할것인지 정의가 가능
let scheduler = MainScheduler.asyncInstance
self.someObservable
  .observe(on: scheduler)
  .retry(
    when: { errorObservable in
      errorObservable.enumerated()
        .flatMap { count, error -> Observable<Void> in
          guard count < 3 else { throw error }
          return Observable<Void>.just(())
            .delay(.seconds(1), scheduler: scheduler)
        }
    }
  )
  .subscribe(
    onNext: { print($0) },
    onError: { print($0) },
    onCompleted: { print("completed") },
    onDisposed: { print("disposed") }
  )
  .disposed(by: self.disposeBag)
  
  
/*
 1
 2
 3
 1
 2
 3
 1
 2
 3
 1
 2
 3
 Error Domain=MyError Code=0 "(null)"
 disposed
 */

cf) 만약 2의 제곱으로 retry하고 싶은 경우, .delay 안의 secnods값에 count값를 이용하여 수정

.retry(
  when: { errorObservable in
    errorObservable.enumerated()
      .flatMap { count, error -> Observable<Void> in
        guard count < 3 else { throw error }
        let retryDyaly = Int(pow(Double(count), 2))
        return Observable<Void>.just(())
          .delay(.seconds(retryDyaly), scheduler: scheduler)
      }
  }
)

* 전체 코드: https://github.com/JK0369/ExRetry

Comments