A good way to deal with mapping network data to your local models in a consistent way, This is how @noremac standardised it in Times codebase
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Combine | |
import Foundation | |
/// This protocols allows you to declare your type as having a distinct network | |
/// representation. | |
/// | |
/// Rather than writing and maintaining a custom `Decodable` implementation for | |
/// your type, declare a brand new struct that exactly matches the expected | |
/// network response, and then write an initializer for your actual type that | |
/// accepts a `NetworkResponse`. | |
/// | |
/// struct Article: NetworkResponseConvertible { | |
/// struct NetworkResponse: Decodable { | |
/// struct Headline: Decodable { | |
/// var default: String | |
/// } | |
/// | |
/// var headline: Headline | |
/// } | |
/// | |
/// var headline: String | |
/// | |
/// init(networkResponse: NetworkResponse) { | |
/// self.headline = networkResponse.headline.default | |
/// } | |
/// } | |
public protocol NetworkResponseConvertible { | |
/// The decoder type. Defaults to `JSONDecoder`. | |
associatedtype Decoder: TopLevelDecoder = JSONDecoder | |
/// The type of your network response. Must be `Decodable`. | |
associatedtype NetworkResponse: Decodable | |
/// Initialize an instance with the given network response. | |
/// - Parameter networkResponse: The network response. | |
init(networkResponse: NetworkResponse) | |
} | |
public extension NetworkResponseConvertible { | |
/// Returns an instance of `Self` decoded from the given input. | |
/// - Parameters: | |
/// - input: The input (generally `Data`). | |
/// - decoder: The decoder. | |
/// - Throws: Decoding errors. | |
/// - Returns: An instance of `Self`. | |
static func decoded(from input: Decoder.Input, decoder: Decoder) throws -> Self { | |
Self(networkResponse: try decoder.decode(NetworkResponse.self, from: input)) | |
} | |
} | |
public extension Publisher where Output == Data { | |
func networkDecode<Value, Decoder>( | |
type _: Value.Type, | |
decoder: Decoder | |
) -> AnyPublisher<Value, Error> | |
where Value: NetworkResponseConvertible, Decoder: TopLevelDecoder, Decoder.Input == Output | |
{ | |
decode(type: Value.NetworkResponse.self, decoder: decoder) | |
.map(Value.init(networkResponse:)) | |
.eraseToAnyPublisher() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment