Skip to content

Instantly share code, notes, and snippets.

@pwwilson
Created March 17, 2022 18:19
Show Gist options
  • Save pwwilson/643fe222477f6a89c6fa5f6f89003c57 to your computer and use it in GitHub Desktop.
Save pwwilson/643fe222477f6a89c6fa5f6f89003c57 to your computer and use it in GitHub Desktop.
Backflip Error Messaging
//
// APIError.swift
// Backflip
//
// Created by Patrick Wilson on 11/16/21.
//
import Foundation
public enum APIError {
case noInternetConnection
case requestEncodingFailed // Request parameters and/or body encoding failed
case responseDecodingFailed // Response object could not be decoded
case badUrl // Could not generate a valid url
case clientErrorStatusCode(statusCode: Int, errorResponse: APIErrorResponse?)
case serverError // Server returned an generic error
case serverReturnedErrorMessage(message: String) // server returned a specific error message
case serverOverload // 503 response
case serverBadGateway // 502 response
case notFound // 404 response
case timeout // 408 response
case unauthorized(requestURL: URL?) // 401 status code
case context // Error occurred w/in View's Context
}
extension APIError: AppError {
public var errorDescription: String? {
switch self {
case .noInternetConnection:
return "no internet connection"
case .requestEncodingFailed:
return "failed to encode request"
case .responseDecodingFailed:
return "failed to decode server response"
case .badUrl:
return "invalid url"
case .clientErrorStatusCode(let statusCode, let errorResponse):
if let response = errorResponse {
return response.error
}
return "client error status code \(statusCode)"
case .serverError:
return "server error"
case .serverReturnedErrorMessage(let message):
return "server generated error message: \(message)"
case .serverBadGateway:
return "bad gateway"
case .serverOverload:
return "server overload"
case .notFound:
return "resource not found"
case .timeout:
return "timeout"
case .unauthorized:
return "unauthorized"
case .context:
return "context error"
}
}
// swiftlint:disable cyclomatic_complexity
public func getUserMessage() -> String {
switch self {
case .noInternetConnection:
return "Sorry, no network connection"
case .requestEncodingFailed, .responseDecodingFailed, .badUrl, .serverError:
return "Sorry, service issues"
case .clientErrorStatusCode(let statusCode, let errorResponse) where statusCode == 500:
return "Sorry, server 500 error \(String(describing: errorResponse))"
case .clientErrorStatusCode(let statusCode, let errorResponse):
if let response = errorResponse {
return "\(response.displayError) (\(statusCode))"
}
let unexpectedMsg = "Sorry: error code"
return "\(unexpectedMsg) \(statusCode)"
case .serverReturnedErrorMessage(let message):
return message
case .serverBadGateway:
return "Sorry, bad gateway (502 error) received"
case .serverOverload:
return "Sorry, high volume/server overload"
case .notFound:
return "Sorry, we could not find the data that you seek"
case .timeout:
return "Sorry, request timed out"
case .unauthorized:
return "Status 401 Unauthorized"
case .context:
return "Sorry, an unknown error has occurred in this context"
}
}
public func getHttpStatusCode() -> Int {
switch self {
case .noInternetConnection:
return 0
case .requestEncodingFailed, .responseDecodingFailed, .badUrl, .context:
return 0
case .clientErrorStatusCode(let statusCode, _):
return statusCode
case .serverReturnedErrorMessage:
return 0
case .serverBadGateway:
return 502
case .serverOverload:
return 503
case .notFound:
return 404
case .timeout:
return 408
case .serverError:
return 0
case .unauthorized:
return 401
}
}
}
// MARK: - Equatable
extension APIError: Equatable {
public static func == (lhs: APIError, rhs: APIError) -> Bool {
switch (lhs, rhs) {
case (let .serverReturnedErrorMessage(lhsMessage), let .serverReturnedErrorMessage(rhsMessage)):
return lhsMessage == rhsMessage
case (let .clientErrorStatusCode(lhsStatusCode, _), let .clientErrorStatusCode(rhsStatusCode, _)):
return lhsStatusCode == rhsStatusCode
case (.noInternetConnection, .noInternetConnection),
(.requestEncodingFailed, requestEncodingFailed),
(.responseDecodingFailed, .responseDecodingFailed),
(.badUrl, .badUrl),
(.serverError, .serverError),
(.serverOverload, .serverOverload),
(.timeout, .timeout):
return true
default:
return false
}
}
}
// MARK: - CustomNSError
extension APIError: CustomNSError {
public static var errorDomain: String {
return String(reflecting: self)
}
public var errorCode: Int {
return self.getHttpStatusCode()
}
public var errorUserInfo: [String: Any] {
var userInfo: [String: Any] = [
NSLocalizedDescriptionKey: self.errorDescription ?? "Error Description Unavailable",
NSLocalizedFailureReasonErrorKey: self.getUserMessage()
]
// When needed, we can set other values in the `userInfo` dictionary
// for specific errors, using `NSError.UserInfoKey` constants as keys.
switch self {
case .unauthorized(let requestURL):
userInfo[NSURLErrorKey] = requestURL?.absoluteString ?? "Request URL unavailable"
default:
break
}
return userInfo
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment