-
-
Save timothyjoel/45d204cde47386b3d847a9f77a87010e to your computer and use it in GitHub Desktop.
Combine - fetching and decoding JSON data
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 Foundation | |
import Combine | |
class Webservice { | |
private let dataFetcher = DataFetcher() | |
func fetch<T: Decodable>(url: URL) -> AnyPublisher<T, WebserviceError> { | |
dataFetcher.fetchData(url: url) | |
.receive(on: RunLoop.main) | |
.decode(type: T.self, decoder: JSONDecoder()) | |
.mapError { error in | |
if let error = error as? DecodingError { | |
var errorToReport = error.localizedDescription | |
switch error { | |
case .dataCorrupted(let context): | |
let details = context.underlyingError?.localizedDescription ?? context.codingPath.map { $0.stringValue }.joined(separator: ".") | |
errorToReport = "\(context.debugDescription) - (\(details))" | |
case .keyNotFound(let key, let context): | |
let details = context.underlyingError?.localizedDescription ?? context.codingPath.map { $0.stringValue }.joined(separator: ".") | |
errorToReport = "\(context.debugDescription) (key: \(key), \(details))" | |
case .typeMismatch(let type, let context), .valueNotFound(let type, let context): | |
let details = context.underlyingError?.localizedDescription ?? context.codingPath.map { $0.stringValue }.joined(separator: ".") | |
errorToReport = "\(context.debugDescription) (type: \(type), \(details))" | |
@unknown default: | |
break | |
} | |
return WebserviceError.parserError(reason: errorToReport) | |
} else { | |
return WebserviceError.apiError(reason: error.localizedDescription) | |
} | |
} | |
.eraseToAnyPublisher() | |
} | |
} | |
class DataFetcher { | |
func fetchData(url: URL) -> AnyPublisher<Data, WebserviceError> { | |
let request = URLRequest(url: url) | |
return URLSession.DataTaskPublisher(request: request, session: .shared) | |
.tryMap { data, response in | |
guard let httpResponse = response as? HTTPURLResponse, 200..<300 ~= httpResponse.statusCode else { | |
throw WebserviceError.responseError(reason: "Couldn't connect to the server") | |
} | |
return data | |
} | |
.mapError { error in | |
if let error = error as? WebserviceError { | |
return error | |
} else { | |
return WebserviceError.apiError(reason: error.localizedDescription) | |
} | |
} | |
.eraseToAnyPublisher() | |
} | |
} | |
enum WebserviceError: Error, LocalizedError { | |
case apiError(reason: String) | |
case parserError(reason: String) | |
case responseError(reason: String) | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment