Skip to content

Instantly share code, notes, and snippets.

@stinger
Created July 3, 2019 12:26
Show Gist options
  • Save stinger/099e640f73fdd027f5790c56a0ee5b37 to your computer and use it in GitHub Desktop.
Save stinger/099e640f73fdd027f5790c56a0ee5b37 to your computer and use it in GitHub Desktop.
Mock data loader using Combine
import Foundation
import Combine
enum APIError: Error, LocalizedError {
case unknownError
case unknownURL
case unknownData
case parserError(reason: String)
case apiError(reason: String)
public var errorDescription: String? {
switch self {
case .parserError(let reason):
return "Parser error: \(reason)"
case .apiError(let reason):
return "API error: \(reason)"
case .unknownData:
return "Unknown data"
case .unknownURL:
return "Unknown URL"
case .unknownError:
return "Unknown error"
}
}
}
enum MockClient: String {
case places = "Places.json"
func fetchData() -> Future<Data, APIError> {
return Future<Data, APIError> { completionHandler in
guard let resource = URL(string: self.rawValue) else {
completionHandler(.failure(.unknownURL))
return
}
guard let url = Bundle.main.url(forResource: resource.deletingPathExtension().absoluteString, withExtension: resource.pathExtension) else {
completionHandler(.failure(.unknownURL))
return
}
DispatchQueue.global(qos: .userInitiated).async {
guard let data = try? Data(contentsOf: url) else {
completionHandler(.failure(.unknownData))
return
}
completionHandler(.success(data))
}
}
}
func fetch<T: Decodable>() -> AnyPublisher<T, APIError> {
self.fetchData()
.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 .parserError(reason: errorToReport)
} else {
return .apiError(reason: error.localizedDescription)
}
}
.eraseToAnyPublisher()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment