Предотвратите завершение Observabe.error и избавьтесь от swift - PullRequest
0 голосов
/ 27 декабря 2018

У меня есть наблюдаемое (запрос из сети), и я не хочу, чтобы оно было удалено, когда я получил ошибку

Моя ошибка

   enum MyError: Error {
        case notFound
        case unknown
    }

Мой сетевой запрос с использованием Мойя

let registerRequest = didTapJoinButton.withLatestFrom(text.asObservable())
            .flatMapLatest { text in
                provider.rx.request(API.register(text: text))
            }
            .flatMapLatest({ (response) -> Observable<Response> in
                let statusCode = response.statusCode

                if statusCode.isSuccessStatus() {
                    return Observable.just(response)

                } else if statusCode.isNotFoundStatus() {
                    return Observable.error(MyError.notFound)

                } else {
                    return Observable.error(MyError.unknown)
                }
            })
            .materialize()
            .share(replay: 1)

Отлично выглядит.Я использую materialize (), чтобы предотвратить появление наблюдаемого при ошибке

Подписаться: (Если код состояния 200) Все работает нормально, я получил ответ, и поток не расположен

   registerEventRequest.subscribe(onNext: { (next) in
            print("NEXT: \(next)")
        }, onError: { (error) in
            print("ERRRRROR ME: \(error)")
        }, onCompleted: {
            print("Completed")
        }) {
            print("Disposed")
        }

НОесли код состояния что-то вроде 404. Я получил ошибку, как я и ожидал.Однако, эй, посмотрите на консольный журнал

NEXT: error(notFound)
Completed
Disposed

Он переходит к следующему, что я ожидал.Но почему он завершает и удаляет мою последовательность.

Мой вопрос: почему он распоряжается моей последовательностью и как я могу предотвратить это?

1 Ответ

0 голосов
/ 27 декабря 2018

.materialize() не исключает предотвращения наблюдаемого от устранения при ошибке.Когда Observable выдает ошибку, она завершается, и материализация просто преобразует эту ошибку в следующее событие.

Вам нужно поместить материализацию внутри первого flatMapLatest, чтобы предотвратить выход ошибки изЗакрытие FlatMap.

Это видео может помочь (примечание selectMany совпадает с flatMap) https://channel9.msdn.com/Blogs/J.Van.Gogh/Reactive-Extensions-API-in-depth-SelectMany?term=select%20many&lang-en=true


Вот еще один способ составить Observable:

let registerRequest = didTapJoinButton.withLatestFrom(text.asObservable())
    .flatMapLatest { text in
        provider.rx.request(API.register(text: text))
            .materialize()
    }
    .map { (event) -> Event<Response> in
        switch event {
        case .next(let response) where response.statusCode.isNotFoundStatus():
            return Event.error(MyError.notFound)

        case .next(let response) where response.statusCode.isSuccessStatus() == false:
            return Event.error(MyError.unknown)

        default:
            return event
        }
    }
    .share(replay: 1)

Я переместился materialize() туда, где он принадлежит, чтобы ошибки не сломали цепочку.Я также поменял второе flatMapLatest на простое map, поскольку дополнительная работа не требовалась.

Оператор switch также можно было бы написать так:

switch event {
case .next(let response):
    let statusCode = response.statusCode
    if statusCode.isNotFoundStatus() {
        return Event.error(MyError.notFound)
    }
    else if statusCode.isSuccessStatus() {
        return event
    }
    else {
        return Event.error(MyError.unknown)
    }

default:
    return event
}

Но я думаю, что способ, которым я это сделал, более чистый, потому что он уменьшает цикломатическую сложность замыкания.


Вот код для решения проблемы, поднятой в комментариях:

extension ObservableType {
    func flatMapLatestT<T, U>(_ selector: @escaping (T) -> Observable<U>) -> Observable<Event<U>>
        where Self.E == Event<T>
    {
        return self.flatMapLatest { (event) -> Observable<Event<U>> in
            switch event {
            case .next(let element):
                return selector(element).materialize()
            case .completed:
                return .just(Event<U>.completed)
            case .error(let error):
                return .just(Event<U>.error(error))
            }
        }
    }
}

В этой сущности содержится целый набор операторов для работы с событиями.https://gist.github.com/dtartaglia/d7b8d5c63cf0c91b85b629a419b98d7e

...