简体   繁体   English

如何映射RxSwift Observable和Result

[英]How to map RxSwift Observable and Result

I have a quick question: 我有一个快速的问题:

  • I have a network request that returns Observable<Result<String, RequestError>> , let's call it requestToken 我有一个返回Observable<Result<String, RequestError>>的网络请求,让我们称之为requestToken
  • if this request succeeds, I want to use the String (token) to do another request that returns Observable<Result<NSDictionary, RequestError>> , let's call it requestData 如果此请求成功,我想使用String (令牌)执行另一个返回Observable<Result<NSDictionary, RequestError>>请求,让我们称之为requestData
  • when that second request comes back, I wanna merge the token into its dictionary 当第二个请求回来时,我想将令牌合并到它的字典中
  • in the end I wanna map from Observable<Result<String, RequestError>> to Observable<Result<NSDictionary, RequestError>> 最后我想从Observable<Result<String, RequestError>>映射到Observable<Result<NSDictionary, RequestError>>

How can I achieve that without multiple nested levels in my code? 如何在代码中没有多个嵌套级别的情况下实现这一目标?

This is what I have today: 这就是我今天所拥有的:

requestToken()
    .flatMap({ result -> Observable<Result<NSDictionary, RequestError>> in
        switch result {
        case .success(let token):
            return requestData(token: token).map({ $0.map({ $0 + ["token": token] }) })
        case .failure(let error):
            return Observable.of(.failure(error))
        }
    })

Updated: 更新:

It's a detailed example, hope this may help: 这是一个详细的例子,希望这可能会有所帮助:

enum RequestError: Error {
    case unknown
}

func requestToken() -> Observable<String> {

    return Observable.create { observer in

        let success = true

        if success {
            observer.onNext("MyTokenValue")
            observer.onCompleted()
        } else {
            observer.onError(RequestError.unknown)
        }

        return Disposables.create()
    }
}

func requestData(token: String) -> Observable<[String: Any]> {

    return Observable<[String: Any]>.create { observer in

        let success = false

        if success {
            observer.onNext(["uid": 007])
            observer.onCompleted()
        } else {
            observer.onError(RequestError.unknown)
        }

        return Disposables.create()
    }
    .map { (data: [String: Any]) in
        var newData = data
        newData["token"] = token
        return newData
    }
}


requestToken()                      // () -> Observable<String>
    .flatMapLatest(requestData)     // Observable<String> -> Observable<[String: Any]>
    .materialize()                  // Observable<[String: Any]> -> Observable<Event<[String: Any]>>
    .subscribe(onNext: { event in
        switch event {
        case .next(let dictionary):
            print("onNext:", dictionary)
        case .error(let error as RequestError):
            print("onRequestError:", error)
        case .error(let error):
            print("onOtherError:", error)
        case .completed:
            print("onCompleted")
        }
    })
    .disposed(by: disposeBag)

Original: 原版的:

I think it's much easier to achieve it using materialize() with less extra work: 我认为使用materialize()以更少的额外工作来实现它要容易得多:

func requestToken() -> Observable<String> { return .empty() }
func requestData(token: String) -> Observable<NSDictionary> { return .empty() }
enum RequestError: Error {}

requestToken()
    .flatMapLatest(requestData)
    .materialize()
    .subscribe(onNext: { event in
        switch event {
        case .next(let dictionary):
            print("onNext:", dictionary)
        case .error(let error as RequestError):
            print("onRequestError:", error)
        case .error(let error):
            print("onOtherError:", error)
        case .completed:
            print("onCompleted")
        }
    })
    .disposed(by: disposeBag)

Hope this may help. 希望这可能有所帮助。

If you use the built in error system, you can save yourself from having to manually pass the error along and all the switches that would entail. 如果使用内置错误系统,则可以避免手动传递错误以及所需的所有开关。 You can cast the error at the end. 你可以在最后施放错误。

I would do something more like this: 我会做更像这样的事情:

// this is necessary to handle adding the token to the dictionary.
extension Dictionary {

    /// An immutable version of update. Returns a new dictionary containing self's values and the key/value passed in.
    func updatedValue(_ value: Value, forKey key: Key) -> Dictionary<Key, Value> {
        var result = self
        result[key] = value
        return result
    }
}

// function signatures, note that they don't return Results anymore.
func requestToken() -> Observable<String> { /*...*/ }
func requestData(withToken: String) -> Observable<[String: Any]> { /*...*/ }

requestToken().flatMapLatest {
    requestData(token: $0)
        .map { $0.updatedValue($0, forKey: "token") }
        .map { .success($0) }
}.catchError {
        Observable.just(.failure($0 as! RequestError))
}

With the above, the end result would be an Observable<Result<[String: Any], RequestError>> just like in your case, but the error handling is much cleaner. 使用上面的结果,最终结果将是Observable<Result<[String: Any], RequestError>> ,就像你的情况一样,但错误处理更清晰。

If you can't change the signatures of the two functions you are using then I would do this: 如果你不能改变你正在使用的两个函数的签名,那么我会这样做:

    func throwError<T, U: Error>(result: Result<T, U>) throws -> T {
        switch result {
        case .success(let token):
            return token
        case .failure(let error):
            throw error
        }
    }

    requestToken().map {
        try throwError(result: $0)
    }.flatMapLatest {
        requestData(token: $0)
            .map { try throwError(result: $0) }
            .map { $0.updatedValue($0, forKey: "token") }
    }
    .map { .success($0) }
    .catchError {
        Observable.just(.failure($0 as! RequestError))
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM