快速组合版本的decodeIfPresent?



我正试图做一些JSON解码我从服务器接收。由于这个应用程序是用SwiftUI编写的,我想我也可以试试Combine。我一直在使用。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()
}
}

相关内容

  • 没有找到相关文章

最新更新