【问题标题】:How to map RxSwift Observable and Result如何映射 RxSwift Observable 和 Result
【发布时间】:2023-04-01 14:20:02
【问题描述】:

我有一个小问题:

  • 我有一个网络请求返回Observable<Result<String, RequestError>>,我们称之为requestToken
  • 如果这个请求成功了,我想用String(token)做另一个返回Observable<Result<NSDictionary, RequestError>>的请求,我们称之为requestData
  • 当第二个请求返回时,我想将令牌合并到它的字典中
  • 最后我想从Observable<Result<String, RequestError>>映射到Observable<Result<NSDictionary, RequestError>>

如果我的代码中没有多个嵌套级别,我该如何实现?

这就是我今天拥有的:

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))
        }
    })

【问题讨论】:

  • 您是否有理由手动返回错误结果而不是使用内置错误系统?
  • 对于类型系统。我需要处理RequestError,而来自Observable 的错误系统只会给我一个通用的Errorthrow 语法相同,它们都没有给我提供函数签名上可能发生的错误的文档。

标签: ios swift rx-swift reactivex


【解决方案1】:

更新:

这是一个详细的例子,希望这可以帮助:

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)

原文:

我认为使用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)

希望这会有所帮助。

【讨论】:

  • 我在 Observable 上找不到 materialize()。它存在于 3.0 版本中吗?
  • 是的,它从 3.4.0 版开始可用 github.com/ReactiveX/RxSwift/releases/tag/3.4.0
  • 我不确定我是否理解materialize 的用途,首先,我的requestToken 返回一个Result,而不是String,所以我不能.flatMapLatest(requestData)。其次,我需要将令牌与requestData 的结果字典合并。你能澄清一下吗?
  • 是的,你看requestToken返回一个Result,很难用ResultflatMap。所以我的代码显示您需要更改requestToken 以返回Observable&lt;String&gt; 而不是Observable&lt;Result&lt;String&gt;&gt;。所以我的代码流程是() -&gt; Observable&lt;String&gt; -&gt; Observable&lt;NSDictionary&gt; -&gt; Observable&lt;Event&lt;NSDictionary&gt;&gt;。而Event 类似于Result,它有三个案例next errorcompletedmaterialize() 的剂量是将 Observable&lt;NSDictionary&gt; 更改为 Observable&lt;Event&lt;NSDictionary&gt;&gt;。所以你可以订阅它。
  • 但是如果requestToken 总是返回一个字符串,我怎么知道它是否会失败?
【解决方案2】:

如果您使用内置的错误系统,您可以避免手动传递错误以及所有可能需要的开关。你可以在最后抛出错误。

我会做更多这样的事情:

// 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))
}

使用上述内容,最终结果将是 Observable&lt;Result&lt;[String: Any], RequestError&gt;&gt;,就像您的情况一样,但错误处理更加清晰。

如果您无法更改正在使用的两个函数的签名,那么我会这样做:

    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))
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-17
    • 1970-01-01
    • 1970-01-01
    • 2012-11-05
    • 2021-12-28
    相关资源
    最近更新 更多