处理 RxSwift 重试和错误处理的最佳实践是什么

Ton*_*Lin 5 error-handling ios swift rx-swift

我读过一些帖子说处理 RxSwift 的最佳实践是只将致命错误传递给 onError 并将结果传递给 onNext。

这对我来说很有意义,直到我意识到我无法再处理重试,因为它只发生在 onError 上。

我该如何处理这个问题?

另一个问题是,如何同时处理全局和本地重试混合?

例如,iOS 收据验证流程。

1、尝试在本地取收据

2、如果失败,请向苹果服务器索取最新的收据。

3,将收据发送到我们的后端进行验证。

4、如果成功,则整个流程完成

5、如果失败,检查错误代码是否可重试,然后返回1。

而在新的 1 中,它将强制要求从苹果服务器获取新收据。然后当它再次达到 5 时,整个流程将停止,因为这已经是第二次尝试了。意思是只重试一次。

因此,在这个例子中,如果使用状态机,并且不使用RX,我会最终使用状态机和共享一些全局状态一样isSecondAttempt: BoolshouldForceFetchReceipt: Bool等等。

我如何在 rx 中设计这个流程?与这些在流程中设计的全局共享状态。

Dan*_* T. 5

我读到一些帖子说处理 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
                }
        }
}
Run Code Online (Sandbox Code Playgroud)

以下是上面使用的支持功能:

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) }
}
Run Code Online (Sandbox Code Playgroud)

  • 有时,一条链可以是更大链的子链。尽管如此,你还是应该独立思考每一个。如果您*不*希望链被处置(即中止),那么您需要使用“materialize()”。但对于 *this* 链来说,错误才是正确的解决方案。如果较大的链*不想*想要中止,我可能会通过 `.flatMap { getReceiptResonse().materialize() }` 在更大的链中使用该链。 (3认同)