source/UberCore/Networking/UberError.swift (270 lines of code) (raw):

// // UberError.swift // UberRides // // Copyright © 2016 Uber Technologies, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // MARK: UberError /// Base class for errors that can be mapped from HTTP responses. @objc(UBSDKError) public class UberError: NSObject, Decodable { /// HTTP status code for error. @objc public internal(set) var status: Int /// Human readable message which corresponds to the client error. @objc public internal(set) var title: String? /// Underscore delimited string. @objc public internal(set) var code: String? /// Additional information about errors. Can be "fields" or "meta" as the key. @objc public internal(set) var meta: [String: Any]? /// List of additional errors. This can be populated instead of status/code/title. @objc public internal(set) var errors: [UberError]? /// Convenience initializer. /// /// - parameter status: The Status code to use for this error /// - parameter code: The underscore delimited code string to use for this error /// - parameter title: Human readable message which corresponds to this error @objc public init(status: Int, code: String?, title: String?) { self.status = status self.code = code self.title = title } public required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) status = try container.decodeIfPresent(Int.self, forKey: .status) ?? -1 title = try container.decodeIfPresent(String.self, forKey: .error) title = try title ?? container.decodeIfPresent(String.self, forKey: .message) title = try title ?? container.decodeIfPresent(String.self, forKey: .title) code = try container.decodeIfPresent(String.self, forKey: .code) errors = try container.decodeIfPresent([UberError].self, forKey: .errors) meta = try? container.decode([String: [String]].self, forKey: .fields) meta = try? meta ?? container.decode([String: [String: String]].self, forKey: .meta) } enum CodingKeys: String, CodingKey { case code = "code" case status = "status" case errors = "errors" case message = "message" case title = "title" case meta = "meta" case fields = "fields" case error = "error" } } // MARK: UberError subclasses /// Client error 4xx. @objc(UBSDKClientError) public class UberClientError: UberError { public required init(from decoder: Decoder) throws { try super.init(from: decoder) } @objc public override init(status: Int, code: String?, title: String?) { super.init(status: status, code: code, title: title) } } /// Server error 5xx. @objc(UBSDKServerError) public class UberServerError: UberError { public required init(from decoder: Decoder) throws { try super.init(from: decoder) } @objc public override init(status: Int, code: String?, title: String?) { super.init(status: status, code: code, title: title) } } /// Unknown error type. @objc(UBSDKUnknownError) public class UberUnknownError: UberError { public required init(from decoder: Decoder) throws { try super.init(from: decoder) } @objc public override init(status: Int, code: String?, title: String?) { super.init(status: status, code: code, title: title) } } // MARK: RidesAuthenticationError /** Possible authentication errors. - AccessDenied: The user denied the requested scopes. - ExpiredJWT: The scope accept session expired. - GeneralError: A general error occured. - InternalServerError: An internal server error occured. - InvalidAppSignature: The provided app signature did not match what was expected. - InvalidAuthCode: There was a problem authorizing you. - InvalidClientID: Invalid client ID provided for authentication. - InvalidFlowError: There was a problem displaying the authorize screen. - InvalidJWT: There was a problem authorizing you. - InvalidJWTSignature: There was a problem authorizing you. - InvalidNonce: There was a problem authorizing you. - InvalidRedirect: Redirect URI provided was invalid - InvalidRefreshToken: The provided Refresh Token was invalid - InvalidRequest: General case for invalid requests. - InvalidResponse: The response from the server was un-parseable - InvalidScope: Scopes provided contains an invalid scope. - InvalidSSOResponse: The server responded with an invalid response. - InvalidUserID: There was a problem with your user ID. - MalformedRequest: There was a problem loading the authorize screen. - MismatchingRedirect: Redirect URI provided doesn't match one registered for client ID. - NetworkError: A network error occured - ServerError: A server error occurred during authentication. - UnableToPresentLogin: Unable to present the login screen - UnableToSaveAccessToken: There was a problem saving the access token - Unavailable: Authentication services temporarily unavailable. - UserCancelled: User cancelled the auth process */ @objc(UBSDKAuthenticationErrorType) public enum UberAuthenticationErrorType: Int { case accessDenied case expiredJWT case generalError case internalServerError case invalidAppSignature case invalidAuthCode case invalidClientID case invalidFlowError case invalidJWT case invalidJWTSignature case invalidNonce case invalidRedirect case invalidRefreshToken case invalidRequest case invalidResponse case invalidScope case invalidSSOResponse case invalidUserID case malformedRequest case mismatchingRedirect case networkError case serverError case unableToPresentLogin case unableToSaveAccessToken case unavailable case userCancelled func toString() -> String { switch self { case .accessDenied: return "access_denied" case .expiredJWT: return "expired_jwt" case .generalError: return "general_error" case .internalServerError: return "internal_server_error" case .invalidAppSignature: return "invalid_app_signature" case .invalidAuthCode: return "invalid_auth_code" case .invalidClientID: return "invalid_client_id" case .invalidFlowError: return "invalid_flow_error" case .invalidJWT: return "invalid_jwt" case .invalidJWTSignature: return "invalid_jwt_signature" case .invalidNonce: return "invalid_nonce" case .invalidRedirect: return "invalid_redirect_uri" case .invalidRefreshToken: return "invalid_refresh_token" case .invalidRequest: return "invalid_parameters" case .invalidResponse: return "invalid_response" case .invalidScope: return "invalid_scope" case .invalidSSOResponse: return "invalid_sso_response" case .invalidUserID: return "invalid_user_id" case .malformedRequest: return "malformed_request" case .mismatchingRedirect: return "mismatching_redirect_uri" case .networkError: return "network_error" case .serverError: return "server_error" case .unableToPresentLogin: return "present_login_failed" case .unableToSaveAccessToken: return "token_not_saved" case .unavailable: return "temporarily_unavailable" case .userCancelled: return "cancelled" } } var localizedDescriptionKey: String { switch self { case .accessDenied: return "The user denied the requested scopes." case .expiredJWT: return "The scope accept session expired." case .generalError: return "A general error occured." case .internalServerError: return "An internal server error occured." case .invalidAppSignature: return "The provided app signature did not match what was expected." case .invalidAuthCode: return "There was a problem authorizing you." case .invalidClientID: return "Invalid Client ID provided." case .invalidFlowError: return "There was a problem displaying the authorize screen." case .invalidJWT: return "There was a problem authorizing you." case .invalidJWTSignature: return "There was a problem authorizing you." case .invalidNonce: return "There was a problem authorizing you." case .invalidRedirect: return "Invalid Redirect URI provided." case .invalidRefreshToken: return "Invalid Refresh Token provided." case .invalidRequest: return "The server was unable to understand your request." case .invalidResponse: return "Unable to interpret the response from the server." case .invalidScope: return "Your app is not authorized for the requested scopes." case .invalidSSOResponse: return "The server responded with an invalid response." case .invalidUserID: return "There was a problem with your user ID." case .malformedRequest: return "There was a problem loading the authorize screen." case .mismatchingRedirect: return "The Redirect URI provided did not match what was expected." case .networkError: return "A network error occured." case .serverError: return "A server error occurred." case .unableToPresentLogin: return "Unable to present the login view." case .unableToSaveAccessToken: return "Unable to save the access token." case .unavailable: return "Login is temporarily unavailable." case .userCancelled: return "User cancelled the login process." } } func toLocalizedDescription() -> String { return NSLocalizedString(localizedDescriptionKey, bundle: Bundle(for: UberError.self), comment: toString()) } } public class UberAuthenticationErrorFactory { static let errorDomain = "com.uber.rides-ios-sdk.ridesAuthenticationError" /** Creates a RidesAuthenticationError for the provided UberAuthenticationErrorType - parameter ridesAuthenticationErrorType: the UberAuthenticationErrorType of error to create - returns: An initialized RidesAuthenticationError */ public static func errorForType(ridesAuthenticationErrorType : UberAuthenticationErrorType) -> NSError { return NSError(domain: errorDomain, code: ridesAuthenticationErrorType.rawValue, userInfo: [NSLocalizedDescriptionKey : ridesAuthenticationErrorType.toLocalizedDescription()]) } public static func createRidesAuthenticationError(rawValue: String) -> NSError? { guard let ridesAuthenticationErrorType = ridesAuthenticationErrorType(rawValue) else { return nil } return UberAuthenticationErrorFactory.errorForType(ridesAuthenticationErrorType: ridesAuthenticationErrorType) } static func ridesAuthenticationErrorType(_ rawValue: String) -> UberAuthenticationErrorType? { switch rawValue { case "access_denied": return .accessDenied case "cancelled": return .userCancelled case "expired_jwt": return .expiredJWT case "general_error": return .generalError case "internal_server_error": return .internalServerError case "invalid_app_signature": return .invalidAppSignature case "invalid_auth_code": return .invalidAuthCode case "invalid_client_id": return .invalidClientID case "invalid_flow_error": return .invalidFlowError case "invalid_jwt": return .invalidJWT case "invalid_jwt_signature": return .invalidJWTSignature case "invalid_nonce": return .invalidNonce case "invalid_parameters": return .invalidRequest case "invalid_redirect_uri": return .invalidRedirect case "invalid_refresh_token": return .invalidRefreshToken case "invalid_response": return .invalidResponse case "invalid_scope": return .invalidScope case "invalid_sso_response": return .invalidSSOResponse case "invalid_user_id": return .invalidUserID case "malformed_request": return .malformedRequest case "mismatching_redirect_uri": return .mismatchingRedirect case "network_error": return .networkError case "present_login_failed": return .unableToPresentLogin case "server_error": return .serverError case "temporarily_unavailable": return .unavailable case "token_not_saved": return .unableToSaveAccessToken default: return nil } } }