与RxSwift联网

时间:2018-10-06 11:59:59

标签: swift networking request rx-swift moya

我一直试图了解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 :(得分:0)

这是我在查看两个代码示例时得出的结论:

首先是使用要点:

    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)的代码。这行代码生成 function networkRequest,该函数接受URLRequest并返回专门用于所讨论类型的Observable。

订阅networkRequest中的Observable时,它将立即推出.loading案例,然后发出请求。然后它将使用响应根据结果推出.loaded(T).error(Error)

我个人更倾向于使用RxSwift存储库中示例中的ActivityIndi​​cator系统之类的东西。