Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

177
Views
What is the best practice to deal with RxSwift retry and error handling

I read some post says that the best practice to deal with RxSwift is to only pass fatal error to the onError and pass Result to the onNext.

It makes sense to me until I realise that I can't deal with retry anymore since it only happen on onError.

How do I deal with this issue?

Another question is, how do I handle global and local retry mixes together?

A example would be, the iOS receipt validation flow.

1, try to fetch receipt locally

2, if failed, ask Apple server for the latest receipt.

3, send the receipt to our backend to validate.

4, if success, then whole flow complete

5, if failed, check the error code if it's retryable, then go back to 1.

and in the new 1, it will force to ask for new receipt from apple server. then when it reaches 5 again, the whole flow will stop since this is the second attempt already. meaning only retry once.

So in this example, if using state machine and without using rx, I will end up using state machine and shares some global state like isSecondAttempt: Bool, shouldForceFetchReceipt: Bool, etc.

How do I design this flow in rx? with these global shared state designed in the flow.

over 4 years ago · Santiago Trujillo
1 answers
Answer question

0

I read some post says that the best practice to deal with RxSwift is to only pass fatal error to the onError and pass Result to the onNext.

I don't agree with that sentiment. It is basically saying that you should only use onError if the programmer made a mistake. You should use errors for un-happy paths or to abort a procedure. They are just like throwing except in an async way.

Here's your algorithm as an Rx chain.

enum ReceiptError: Error {
    case noReceipt
    case tooManyAttempts
}

struct Response { 
    // the server response info
}

func getReceiptResonse() -> Observable<Response> {
    return fetchReceiptLocally()
        .catchError { _ in askAppleForReceipt() }
        .flatMapLatest { data in
            sendReceiptToServer(data)
        }
        .retryWhen { error in
            error
                .scan(0) { attempts, error in
                    let max = 1
                    guard attempts < max else { throw ReceiptError.tooManyAttempts }
                    guard isRetryable(error) else { throw error }
                    return attempts + 1
                }
        }
}

Here are the support functions that the above uses:

func fetchReceiptLocally() -> Observable<Data> {
    // return the local receipt data or call `onError`
}

func sendReceiptToServer(_ data: Data) -> Observable<Response> {
    // send the receipt data or `onError` if the server failed to receive or process it correctly.
}

func isRetryable(_ error: Error) -> Bool {
    // is this error the kind that can be retried?
}

func askAppleForReceipt() -> Observable<Data> {
    return Observable.just(Bundle.main.appStoreReceiptURL)
        .map { (url) -> URL in
            guard let url = url else { throw ReceiptError.noReceipt }
            return url
        }
        .observeOn(ConcurrentDispatchQueueScheduler(qos: .userInitiated))
        .map { try Data(contentsOf: $0) }
}
over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!