问题描述
我正在尝试对从服务器接收到的 JSON 进行一些解码。因为这个应用程序是用 SwiftUI 编写的,所以我想我也可以试试结合。我一直在使用 .decode() 作为我的组合链的一部分,它一直运行良好,但现在我需要解码 json ,这将无法使用。
我正在尝试将格式的 JSON 解码为 Team
结构。然而,问题是这不能保证存在于服务器上,在这些情况下,服务器只是不返回 JSON(但是它仍然具有正确的 HTTPS 响应代码,所以我知道什么时候是这种情况)。我的问题是如何将接收到的数据解码为可选的 Team?
(如果没有收到 JSON,则它是解码的团队数据或 nil。
struct Team: Codable,Identifiable,Hashable {
var id: UUID
var name: String
var currentrating: Int
enum CodingKeys: String,CodingKey {
case id = "id"
case name = "name"
case currentrating = "rating"
}
}
func fetch<T: Decodable>(
from endpoint: Endpoint,with decoder: JSONDecoder = JSONDecoder()
) -> AnyPublisher<T,DatabaseError> {
// Get the URL from the endpoint
guard let url = endpoint.url else { ... }
let request = URLRequest(url: url)
// Get the publisher data from the server
// retrieveData is a function with the return type AnyPublisher<Data,DatabaseError>
return retrieveData(with: request)
// Try to decode into a decodable object
.decode(type: T.self,decoder: decoder)
// If there is an error,map it to a DatabaseError
.mapError { ... }
// Main thread
.receive(on: dispatchQueue.main)
// Type erase
.erasetoAnyPublisher()
}
解决方法
理想情况下,您可以检查服务器响应并决定您想要做什么,比如给定一个特定的 HTTP 代码或者如果 data
为空,但在您的情况下,retrieveData
只是给您数据 -所以,这里没什么可玩的。
您可以尝试解码,如果失败,则返回 nil
:
return retrieveData(with: request)
.flatMap {
Just($0)
.decode(type: T?.self,decoder: decoder)
.replaceError(with: nil)
}
.mapError { ... }
//... etc
上面的缺点是它会隐藏任何实际的解码错误,比如类型不匹配,所以你可以更精确地处理并且只在数据不为空时解码。
这是一个可能的 decodeIfPresent
实现:
extension Publisher where Output == Data {
func decodeIfPresent<T: Decodable,Coder: TopLevelDecoder>(
type: T.Type,decoder: Coder
) -> AnyPublisher<T?,Error> where Coder.Input == Output {
self.mapError { $0 as Error }
.flatMap { d -> AnyPublisher<T?,Error> in
if d.isEmpty {
return Just<T?>(nil)
.setFailureType(to: Error.self)
.eraseToAnyPublisher()
} else {
return Just(d)
.decode(type: T?.self,decoder: decoder)
.eraseToAnyPublisher()
}
}
.eraseToAnyPublisher()
}
}