【问题标题】:Combine sink does not complete when publisher fails发布者失败时合并接收器未完成
【发布时间】:2020-05-05 16:03:22
【问题描述】:

我目前正在构建一个 SwiftUI 应用程序并使用来自 github (GitHub/Squid) 的网络库来实现 api 调用。

它工作正常,除非请求失败,然后库内部调试消息指示错误(在我的测试中为 404),但合并订阅者从未收到完成信号。

我创建了一个新请求,它将尝试获取和解码“jsonplaceholder.typicode.com”的用户。

import Foundation
import Squid

struct GetUsersFromMockApiRequest: JsonRequest {
    typealias Result = [User]

    var routes: HttpRoute {
        ["users"]
    }
}

User 只是一个简单的结构体:

struct User: Decodable {
    let id: Int
    let username: String
    let name: String
}

在我的 ApiRequestManager.swift 文件中,我声明了一个函数来请求调度:

public func getUsersFromMockApi() -> Response<GetUsersFromMockApiRequest> {
    return GetUsersFromMockApiRequest().schedule(with: ApiService(apiUrl: "jsonplaceholder.typicode.com"))
}

在我想调用 api 的地方,我有一个可取消集和一个 ApiRequestManager 实例。

@State private var cancellableSet: Set<AnyCancellable> = []
private let requestManager = ApiRequestManager()

现在最后一步是在按下按钮时调用一个函数,该函数执行以下代码:

self.requestManager.getUsersFromMockApi()
  .print("Debug:")
  .sink(receiveCompletion: { completion in
    switch completion {
    case .failure:
      print("ApiCall failed.")
    case .finished:
      print("ApiCall finished.")
    }
  }) { result in
    print("Received users from apiCall: \(result)")
  }
  .store(in: &cancellableSet)

如果 api 请求成功,一切正常。但是一旦它遇到任何错误,接收器就会收到任何信号/完成。

我尝试打印订阅者收到的每条消息,breakOnError 并一步一步地跟踪库以找出他是否甚至抛出错误。它会根据 NetworkScheduler.swift 文件中 schedule() 函数中接受的状态代码正确检查 HTTP 响应代码,并在第 285 行抛出自定义错误 throw Squid.Error.requestFailed(statusCode: statusCode, response: response.data)

但是,由于我缺乏调试和组合知识,我很难在之后进行投掷。

【问题讨论】:

    标签: network-programming swiftui combine


    【解决方案1】:

    您看到的问题可能是 404 不被底层 Cocoa 库(特别是 URLSession)视为错误。这似乎是 Squid 库中正在执行(或未执行)的操作,您正在对其提供的内容做出反应。

    URLSession 本身仅在无法从服务器获得响应时引发错误 - 因此,当问题与无法解析主机名或建立连接等相关时。如果您确实建立了连接并获得了响应,则 URLSession 不会抛出错误,但会正确反映状态代码 - 它只会“看起来”像一个成功的请求。

    上面的示例没有显示该部分,但是来自URLSession 的 404 响应发生的情况是请求完成,并且状态代码 404 被编码在响应中,但它不会作为错误抛出状态。

    在组合发布者链中处理此问题的典型模式是将 URLSession 中的结果数据传递给 tryMap 运算符,您可以在其中进一步检查结果(状态代码、数据等)并确定是否你想把它变成一个抛出的错误(比如如果你得到一个 404 响应)。

    您可以在https://heckj.github.io/swiftui-notes/#patterns-datataskpublisher-trymap 找到此类模式的示例。这种模式的要点是允许您根据对结果的检查来定义您想要的任何错误(假设您在这些情况下抛出异常)。

    您可以在 Using Combine 中找到使用 Combine(以及 URLSession.dataTaskPublisher)的其他示例。

    【讨论】:

    • 伟大的洞察力,是的,这就是问题所在。我联系了图书馆的开发人员,他似乎只是错过了 404 案例。他很快修复了它,现在一切正常!感谢您的帮助。
    猜你喜欢
    • 1970-01-01
    • 2021-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多