我读过一些文章说,处理RxSwift的最佳实践是仅将致命错误传递给onError,并将Result传递给onNext。
这对我来说很有意义,直到我意识到我无法处理重试了,因为它仅发生在onError上。
如何处理此问题?
另一个问题是,如何将全局和本地重试混合在一起?
一个例子就是iOS收据验证流程。
1,尝试在本地获取收据
2,如果失败,请向Apple服务器索取最新收据。
3,将收据发送到我们的后端以进行验证。
4,如果成功,则整个流程完成
5,如果失败,请检查错误代码是否可以重试,然后返回1。
,在新的1中,它将强制从Apple服务器请求新的收据。然后当它再次达到5时,整个流程将停止,因为这已经是第二次尝试了。表示只重试一次。
因此在此示例中,如果使用状态机而不使用rx,我将最终使用状态机并共享一些全局状态,例如isSecondAttempt: Bool
,shouldForceFetchReceipt: Bool
等。
如何在rx中设计此流程?在流程中设计了这些全局共享状态。
答案 0 :(得分:1)
我读过一些文章说,处理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) }
}