Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save timothyjoel/45d204cde47386b3d847a9f77a87010e to your computer and use it in GitHub Desktop.
Save timothyjoel/45d204cde47386b3d847a9f77a87010e to your computer and use it in GitHub Desktop.
Combine - fetching and decoding JSON data
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