Сеть с RxSwift - PullRequest
       33

Сеть с RxSwift

0 голосов
/ 06 октября 2018

Я пытался понять rxSwift.Я столкнулся с проблемой запроса и хочу реализовать это хорошим способом.В настоящее время я использую этот код:

enum RequestState<T> {
    case loading
    case loaded(T)
    case error(Error)
}

struct Response<T: Decodable>: Decodable {
    let data: T
    let error: ResponseError?
}

searchBar.rx.text.asObservable()
    .flatMap { self.provider.rx.request(Request(query: $0)) }
    .map({ RequestState<Response<Bool>>.loaded($0) })
    .asDriver(onErrorRecover: { error in
        return Driver.just(.error(error))
    })
    .startWith(.loading)
    .drive(onNext: { state in
        switch state {
        case .loading: ()
        case .loaded(let response): ()
        case .error(let error): ()
        }
    })
    .disposed(by: disposeBag)

Это хорошо работает, но не слишком удобно для работы с данными и состоянием запроса.Я видел в демонстрационном проекте rxSwift следующий код:

struct RequestState<T: Decodable> {
    let isLoading: Bool
    let data: T
    let error: ResponseError?
}

let state = viewModel.requestMethod()

state
    .map { $0.isLoading }
    .drive(self.loaderView.isOffline)
    .disposed(by: disposeBag)

state
    .map { $0.data }
    .drive(tableView.rx.items(dataSource: dataSource))
    .disposed(by: disposeBag)

state
    .map { $0.error }
    .drive(onNext: { error in
        showAlert(error)
    })
    .disposed(by: disposeBag)

И моя проблема в следующем методе, я не могу понять магию Rx здесь:

func requestMethod() -> Driver<RequestState> {
    // supper code
}

Может кто-нибудь посоветовать мне, что яздесь делать?

1 Ответ

0 голосов
/ 06 октября 2018

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

Сначала вот точка использования:

    let request = searchBar.rx.text
        .unwrap()
        .map { URLRequest.search(forQuery: $0) }

    let networkRequest = createRequest(forType: MyType.self)
    let state = request
        .flatMap(networkRequest)

    state
        .map { $0.isLoading }
        .bind(to: loaderView.isOffline)
        .disposed(by: bag)

    state
        .map { $0.data }
        .unwrap()
        .bind(to: tableView.rx.items(dataSource: dataSource))
        .disposed(by: bag)

    state
        .map { $0.error }
        .unwrap()
        .subscribe(onNext: showAlert)
        .disposed(by: bag)

Вот код поддержки для выше:

enum RequestState<T> {
    case loading
    case loaded(T)
    case error(Error)

    var isLoading: Bool {
        guard case .loading = self else { return false }
        return true
    }

    var data: T? {
        guard case let .loaded(t) = self else { return nil }
        return t
    }

    var error: Error? {
        guard case let .error(e) = self else { return nil }
        return e
    }
}

Вы увидите, что вышеприведенное перечисление RequestState является объединением обоих типов RequestState, которые вы показали в своем примере.Перечисление облегчает создание объекта, в то время как вычисленные свойства облегчают извлечение информации.

func createRequest<T>(forType type: T.Type, session: URLSession = URLSession.shared) -> (URLRequest) -> Observable<RequestState<T>> where T: Decodable {
    return { request in
        return Observable.create { observer in
            observer.onNext(.loading)
            let disposable = session.rx.data(request: request)
                .subscribe { event in
                    switch event {
                    case let .error(error):
                        observer.onNext(.error(error))
                    case let .next(data):
                        do {
                            let item = try JSONDecoder().decode(type, from: data)
                            observer.onNext(.loaded(item))
                        }
                        catch {
                            observer.onNext(.error(error))
                        }
                    case .completed:
                        observer.onCompleted()
                    }
            }
            return Disposables.create([disposable])
        }
    }
}

Выше приведена заводская функция.Вы используете его для создания функции, которая знает, как делать сетевые запросы для соответствующего типа.Вспомните в коде, где он используется, у меня был let networkRequest = createRequest(forType: MyType.self).Эта строка создает функцию networkRequest, которая принимает URLRequest и возвращает Observable, специализированный для рассматриваемого типа.

Когда на Observable из networkRequest подписана, онанемедленно вытолкнет .loading чехол, затем сделает запрос.Затем он будет использовать ответ для выталкивания .loaded(T) или .error(Error) в зависимости от результатов.

Лично я более склонен использовать что-то вроде системы ActivityIndicator из примеров в RxSwiftвместо хранилища.

...