【问题标题】:Swift Combine: handle no data before decode without an errorSwift Combine:在解码前不处理任何数据而不会出错
【发布时间】:2023-10-15 07:46:02
【问题描述】:

我的 API 通常以 JSON 格式返回某种格式(简化符号):

{
   status: // http status
   error?: // error handle
   data?:  // the response data
   ...
}

在我的组合运算符中,我从 URLSession dataTaskPublisher 中获取 data 并将响应解析为反映上述模式的 Decodable 对象。效果很好。

但是,我有一个端点,它返回 HTTP 状态代码 201(操作成功),并且根本没有没有数据。我如何将其与我的操作员链接而不抛出错误

这就是我所拥有的:

publisher
.map { (data, response) in
    guard data.count > 0 else {
       let status = (response as! HTTPURLResponse).statusCode
       return Data("{\"status\": \(status), \"data\": \"\"}".utf8)
    }
    return data
}
.mapError { CustomError.network(description: "\($0)")}
.decode(type: MyResponse<R>.self, decoder: self.agent.decoder)
.mapError { err -> CustomError in CustomError.decoding(description: "\(err)") }
...

如您所见,我只是构造了一个适当的响应,其中响应的“数据”是一个空字符串。然而,这很丑陋而且有点老套,当我已经拥有了我需要的一切时,我不明白为什么管道应该继续解析等原因。如何中断它并为其最终订阅者成功完成管道?

【问题讨论】:

    标签: swift operator-keyword combine publisher


    【解决方案1】:

    我建议创建一个单独的Publisher 来处理不返回任何Data 的特定端点。您可以使用 tryMap 检查 HTTP 状态代码,并在它不在接受范围内时抛出错误。如果您不关心结果,只关心成功响应,您可以映射到Void。如果你关心结果(或状态码),你也可以映射到那个。

    extension URLSession.DataTaskPublisher {
        func emptyBodyResponsePublisher() -> AnyPublisher<Void, CustomError> {
            tryMap { _, response in
                guard let httpResponse = response as? HTTPURLResponse else { throw CustomError.nonHTTPResponse }
                let statusCode = httpResponse.statusCode
                guard (200..<300).contains(statusCode) else { throw CustomError.incorrectStatusCode(statusCode) }
                return Void()
            }.mapError { CustomError.network($0) }
            .eraseToAnyPublisher()
        }
    }
    

    【讨论】:

    • 好的,那行得通。我正在尝试将其从调用者那里抽象出来...如果需要空数据,可能会在我的 Endpoint 中添加一个附加功能。
    • @Mundi 如果你想将它从调用者那里抽象出来,你需要确保接口是相同的——这会产生一个像你已经想出的那样的变通方法。您要么需要修改端点(如果可以的话),要么接受调用者需要知道此特定端点不返回任何数据的事实——这应该不是问题,因为 API 的使用者应该知道什么格式每个特定的端点都会返回。
    最近更新 更多