如何在 Swift 中使用 Combine 读取 JSON 错误对象的属性值?

Posted

技术标签:

【中文标题】如何在 Swift 中使用 Combine 读取 JSON 错误对象的属性值?【英文标题】:How do I read the property values of a JSON error object using Combine in Swift? 【发布时间】:2020-05-28 18:25:13 【问题描述】:

我所有的 API 端点都返回一个在 Postman 中看起来像这样的响应:


    "statusCode": 401,
    "error": "Unauthorized",
    "message": "Missing authentication"

我想做的是发出请求,并在 Swift 中访问这些属性。在某些情况下,我会在应用程序的前面使用错误消息属性的值。这将由返回的 statusCode 确定。

我现在拥有的是这样的:

    private var cancellable: AnyCancellable?
    let url = URL(string: "http://abc336699.com/create")
    self.cancellable = URLSession.shared.dataTaskPublisher(for: url!)
      .map  $0.data 

在此之前,我尝试过 tryMap,但它返回的错误类型并没有给我想要的灵活性。然后我继续尝试 Almofire,但对于我想做的事情来说,这似乎是一种过度杀戮。

我想检查我收到的响应中返回的内容,但我收到以下错误:

Cannot assign value of type 'Publishers.Map<URLSession.DataTaskPublisher, Data>' to type 'AnyCancellable'

我想要简单地访问我的响应错误,以便我可以使用 combine 将 API 集成到整个应用程序中。

【问题讨论】:

AnyCancellable 是一种通常由订阅者向发布者实现的协议,例如在.sink.assign 中使用的协议。 .map 是另一个发布者(不是订阅者)。如果需要处理错误,可以使用.catch 您为什么不根据您的 JSON 响应创建可解码模型并使用 Combine 中的解码运算符对其进行解码? @user832 如果在 200 statusCode 响应后有数据要解码,那很好,但我也需要捕获错误。 @LondonGuy,这应该可以帮助你:developer.apple.com/documentation/foundation/urlsession/… @LondonGuy 请记住,非 200 状态计数是网络成功。如果请求从未得到服务器的响应,数据任务发布者只会出错。如果服务器返回非 200 状态代码,您可以将其提取到 map 【参考方案1】:

我不确定您将从哪里获取数据,因为在 JSON 响应中没有数据密钥。在写下面的代码之前,我的理解是你想从提到的 JSON 响应中检查 errorstatusCode,然后继续你的业务逻辑。下面的代码是为了让您大致了解我们如何做到这一点。

    enum CustomError: Error 

    case custom(_ error: String)
    case unknownStatusCode
    case errorOccurred


let url = URL(string: "http://abc336699.com/create")

    func load() -> AnyPublisher<Data,CustomError> 
    URLSession.shared.dataTaskPublisher(for: url!)
        .map(\.data)
        .tryMap  (data) -> Data in
            let genericModel = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: AnyObject]

            if let statusCode = genericModel?["statusCode"] as? String 
                switch statusCode 
                case "200":
                    guard let data = genericModel?["message"] as? Data else 
                        throw CustomError.custom("Parsing error")
                    
                    return data
                default:
                    if let error = genericModel?["error"] as? String 
                        throw CustomError.custom(error)
                     else 
                        throw CustomError.unknownError
                    
                
            
            throw CustomError.errorOccurred
    
    .decode(type: YourCustomDecodableModel.self, decoder: JSONDecoder())
    .mapError( $0 as? CustomError ?? CustomError.errorOccurred )
    .eraseToAnyPublisher()

【讨论】:

它的工作原理是,如果状态码不是200,我的问题中的对象会显示,如果状态码是200,会显示相同的对象,但状态码会是200,错误将是“成功”,消息将是数据,例如名字或其他东西的列表。 @LondonGuy 我根据您的评论更新了加载方法 这让我找到了答案:heckj.github.io/swiftui-notes/#reasoning-about-pipelines

以上是关于如何在 Swift 中使用 Combine 读取 JSON 错误对象的属性值?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Swift Combine 中创建自定义链?

Swift Combine:如何从发布者列表中创建单个发布者?

如何通过另一个可观察对象观察已发布的属性 - Swift Combine

相当于在 Swift Combine 中使用 @Published 计算的属性?

Xcode 11 中 Swift Combine.framework 的可选链接

如何使用 combine Publisher 更改线程?