处理 RxSwift 重试和错误处理的最佳实践是什么
What is the best practice to deal with RxSwift retry and error handling
我读到一些 post 说处理 RxSwift 的最佳实践是只将致命错误传递给 onError 并将结果传递给 onNext。
这对我来说很有意义,直到我意识到我无法再处理重试,因为它只发生在 onError 上。
我该如何处理这个问题?
另一个问题是,如何处理全局和本地重试混合在一起?
例如,iOS 收据验证流程。
1、尝试本地取收据
2、如果失败,向苹果服务器索取最新回执。
3、将收据发送到我们的后台进行验证。
4、如果成功,则整个流程完成
5、如果失败,检查错误代码是否可重试,然后返回1。
而在新的1中,它会强制向苹果服务器索取新的收据。然后当它再次达到 5 时,整个流程将停止,因为这已经是第二次尝试了。意思是只重试一次。
所以在这个例子中,如果使用状态机而不使用 rx,我将最终使用状态机并共享一些全局状态,如 isSecondAttempt: Bool
、shouldForceFetchReceipt: Bool
等。
我如何在 rx 中设计这个流程?这些全局共享状态是在流程中设计的。
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.
我不同意这种观点。它基本上是说如果程序员犯了错误,你应该只使用 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: [=11=]) }
}
我读到一些 post 说处理 RxSwift 的最佳实践是只将致命错误传递给 onError 并将结果传递给 onNext。
这对我来说很有意义,直到我意识到我无法再处理重试,因为它只发生在 onError 上。
我该如何处理这个问题?
另一个问题是,如何处理全局和本地重试混合在一起?
例如,iOS 收据验证流程。
1、尝试本地取收据
2、如果失败,向苹果服务器索取最新回执。
3、将收据发送到我们的后台进行验证。
4、如果成功,则整个流程完成
5、如果失败,检查错误代码是否可重试,然后返回1。
而在新的1中,它会强制向苹果服务器索取新的收据。然后当它再次达到 5 时,整个流程将停止,因为这已经是第二次尝试了。意思是只重试一次。
所以在这个例子中,如果使用状态机而不使用 rx,我将最终使用状态机并共享一些全局状态,如 isSecondAttempt: Bool
、shouldForceFetchReceipt: Bool
等。
我如何在 rx 中设计这个流程?这些全局共享状态是在流程中设计的。
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.
我不同意这种观点。它基本上是说如果程序员犯了错误,你应该只使用 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: [=11=]) }
}