Skip to content

Instantly share code, notes, and snippets.

@stinger
Created October 28, 2021 13:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save stinger/ddf2add9d2158f89ac3562223770afae to your computer and use it in GitHub Desktop.
Save stinger/ddf2add9d2158f89ac3562223770afae to your computer and use it in GitHub Desktop.
Swift Concurrency fetcher
enum APIError: Error, LocalizedError {
case unknown, apiError(reason: String), parserError(reason: String)
var errorDescription: String? {
switch self {
case .unknown:
return "Unknown error"
case .apiError(let reason), .parserError(let reason):
return reason
}
}
}
struct Activity: Decodable, CustomStringConvertible {
var activity: String
var description: String {
return activity
}
}
func fetch(url: URL) async throws -> Data {
let request = URLRequest(url: url)
do {
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse, 200..<300 ~= httpResponse.statusCode else {
throw APIError.unknown
}
return data
} catch let error {
if let error = error as? APIError {
throw error
} else {
throw APIError.apiError(reason: error.localizedDescription)
}
}
}
func fetch<T: Decodable>(url: URL) async throws -> T {
do {
let data = try await fetch(url: url)
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
} catch let error {
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
}
throw APIError.parserError(reason: errorToReport)
} else {
throw APIError.apiError(reason: error.localizedDescription)
}
}
}
// Usage
if let url = URL(string: "https://www.boredapi.com/api/activity") {
_ = Task {
do {
let activity: Activity = try await fetch(url: url)
print("Suggested activity: \(activity)")
} catch let error {
print("Error: \(error.localizedDescription)")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment