Какова лучшая практика, чтобы иметь дело с повторной попыткой RxSwift и обработкой ошибок - PullRequest
0 голосов
/ 19 февраля 2019

Я прочитал, что в одном сообщении говорится, что лучшая практика для работы с RxSwift - передавать только фатальную ошибку в onError и передавать Result в onNext.

Это имеет смысл для меня, пока я не пойму, что могу 'больше не иметь дело с повторной попыткой, так как это происходит только в onError.

Как мне решить эту проблему?

Другой вопрос: как обрабатывать глобальные и локальные повторные миксы?

Примером может служить проверка квитанции iOS.flow.

1, попробуйте получить квитанцию ​​локально

2, если не удалось, запросите последнюю квитанцию ​​у сервера Apple.

3, отправьте квитанцию ​​на наш сервер для проверки.

4, если успех, то весь поток завершен

5, если не удалось, проверьте код ошибки, если он повторяется, затем вернитесь к 1.

и вnew 1, это заставит запрашивать новую квитанцию ​​с сервера Apple.затем, когда он снова достигнет 5, весь поток остановится, поскольку это уже вторая попытка.значение только повторяется.

Так что в этом примере, если используется конечный автомат и без использования rx, я в конечном итоге использую конечный автомат и разделяет некоторые глобальные состояния, такие как isSecondAttempt: Bool, shouldForceFetchReceipt: Bool и т. д.

Как мне спроектировать этот поток в rx?с этими глобальным общим состоянием, разработанным в потоке.

1 Ответ

0 голосов
/ 19 февраля 2019

Я прочитал, что в некоторых сообщениях говорится, что лучшая практика для работы с 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) }
}
...