【问题标题】:Combine turn one Publisher into another将一个发布者合并为另一个发布者
【发布时间】:2019-11-30 20:30:35
【问题描述】:

我使用 OAuth 框架,它像这样异步创建经过身份验证的请求:

OAuthSession.current.makeAuthenticatedRequest(request: myURLRequest) { (result: Result<URLRequest, OAuthError>) in
            switch result {
            case .success(let request):
                URLSession.shared.dataTask(with: request) { (data, response, error) in
                    // ...
                }
             // ...
             }
        }

我正在尝试让我的 OAuth 框架使用 Combine,所以我知道有一个发布者版本的 makeAuthenticatedRequest 方法,即:

public func makeAuthenticatedRequest(request: URLRequest) -> AnyPublisher<URLRequest, OAuthError>

我正在尝试使用它来替换上面的调用站点,如下所示:

OAuthSession.current.makeAuthenticatedRequestPublisher(request)
    .tryMap(URLSession.shared.dataTaskPublisher(for:))
    .tryMap { (data, _) in data } // Problem is here
    .decode(type: A.self, decoder: decoder)

如上所述,问题在于将发布者的结果转换为新的发布者。我该怎么做?

【问题讨论】:

    标签: ios swift combine


    【解决方案1】:

    您需要使用flatMap,而不是tryMap,围绕dataTaskPublisher(for:)

    查看类型。从这个开始:

    let p0 = OAuthSession.current.makeAuthenticatedRequest(request: request)
    

    按住 Option 键单击 p0 以查看其推断类型。它是AnyPublisher&lt;URLRequest, OAuthError&gt;,因为这就是makeAuthenticatedRequest(request:) 被声明要返回的内容。

    现在添加:

    let p1 = p0.tryMap(URLSession.shared.dataTaskPublisher(for:))
    

    按住 Option 键单击 p1 以查看其推导类型 Publishers.TryMap&lt;AnyPublisher&lt;URLRequest, OAuthError&gt;, URLSession.DataTaskPublisher&gt;。哎呀,这有点难以理解。使用eraseToAnyPublisher 进行简化:

    let p1 = p0
        .tryMap(URLSession.shared.dataTaskPublisher(for:))
        .eraseToAnyPublisher()
    

    现在p1 的推断类型是AnyPublisher&lt;URLSession.DataTaskPublisher, Error&gt;。里面仍然有一个有点神秘的类型URLSession.DataTaskPublisher,所以让我们也删除它:

    let p1 = p0.tryMap {
        URLSession.shared.dataTaskPublisher(for: $0)
            .eraseToAnyPublisher() }
        .eraseToAnyPublisher()
    

    现在Xcode可以告诉我们p1的推断类型是AnyPublisher&lt;AnyPublisher&lt;URLSession.DataTaskPublisher.Output, URLSession.DataTaskPublisher.Failure&gt;, OAuthError&gt;。让我重新格式化以提高可读性:

    AnyPublisher<
        AnyPublisher<
            URLSession.DataTaskPublisher.Output, 
            URLSession.DataTaskPublisher.Failure>,
        OAuthError>
    

    这是一个发布者发布URLSession.DataTaskPublisher.Output的发布者。

    这不是您所期望的,这也是您的第二个 tryMap 失败的原因。您以为您正在创建 URLSession.DataTaskPublisher.Output 的发布者(这是元组 (data: Data, response: URLResponse)typealias),这就是您的第二个 tryMap 想要的输入。但 Combine 认为你的第二个 tryMap 的输入应该是 URLSession.DataTaskPublisher

    当您看到这种嵌套,以及发布发布者的发布者时,这意味着您可能需要使用flatMap 而不是map(或tryMap)。让我们这样做:

    let p1 = p0.flatMap {
           //   ^^^^^^^ flatMap instead of tryMap
        URLSession.shared.dataTaskPublisher(for: $0)
            .eraseToAnyPublisher() }
        .eraseToAnyPublisher()
    

    现在我们得到一个编译时错误:

    ? 实例方法 'flatMap(maxPublishers:_:)' 要求类型 'OAuthError' 和 'URLSession.DataTaskPublisher.Failure'(又名 'URLError')是等价的

    问题是由于外部发布者的失败类型为OAuthError,而内部发布者的失败类型为URLError,Combine 无法展平嵌套。如果它们具有相同的故障类型,Combine 只能将它们展平。我们可以通过将两种故障类型转换为通用的Error 类型来解决这个问题:

    let p1 = p0
        .mapError { $0 as Error }
        .flatMap {
            URLSession.shared.dataTaskPublisher(for: $0)
                .mapError { $0 as Error }
                .eraseToAnyPublisher() }
        .eraseToAnyPublisher()
    

    这样编译,Xcode 告诉我们推导的类型是AnyPublisher&lt;URLSession.DataTaskPublisher.Output, Error&gt;,这就是我们想要的。我们可以添加您的下一个 tryMap,但我们只需使用 map,因为正文不会抛出任何错误:

    let p2 = p1.map { $0.data }.eraseToAnyPublisher()
    

    Xcode 告诉我们p2 是一个AnyPublisher&lt;Data, Error&gt;,所以我们可以链接一个decode 修饰符。

    现在我们已经理顺了类型,我们可以摆脱所有类型的橡皮擦并将它们放在一起:

    OAuthSession.current.makeAuthenticatedRequest(request: request)
        .mapError { $0 as Error }
        .flatMap {
            URLSession.shared.dataTaskPublisher(for: $0)
                .mapError { $0 as Error } }
        .map { $0.data }
        .decode(type: A.self, decoder: decoder)
    

    【讨论】:

    • 我希望我能给这个答案超过 1 票。它非常清脆清晰!谢谢你,抢
    猜你喜欢
    • 1970-01-01
    • 2021-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-24
    • 2023-03-23
    • 1970-01-01
    相关资源
    最近更新 更多