我读了一些帖子说处理RxSwift的最佳做法是只将致命错误传递给onError并将Result传递给onNext。
这对我有意义,直到我意识到我不能再处理重试,因为它只发生在onError上。
我该如何处理这个问题?
另一个问题是,我如何一起处理全局和本地重试混合?
一个例子是iOS收据验证流程。
1,尝试在本地获取收据
2,如果失败,请向Apple服务器索取最新收据。
3,将收据发送到我们的后端进行验证。
4,如果成功,那么整个流程完成
5,如果失败,检查错误代码是否可以重试,然后返回1。
在新的1中,它将强制要求苹果服务器的新收据。然后当它再次达到5时,整个流程将停止,因为这已经是第二次尝试了。意思是只重试一次。
所以在这个例子中,如果使用状态机而不使用rx,我将最终使用状态机并共享一些全局状态,如isSecondAttempt: Bool
,shouldForceFetchReceipt: Bool
等。
如何在rx中设计此流程?在流程中设计这些全局共享状态。
我读了一些帖子说处理RxSwift的最佳做法是只将致命错误传递给onError并将Result传递给onNext。
我不同意这种观点。基本上说,如果程序员犯了错误,你应该只使用onError
。您应该将错误用于不满意的路径或中止过程。它们就像投掷一样,只是以异步的方式。
这是你的算法作为Rx链。
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
}
}
}
以下是上述用途的支持功能:
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) }
}