thrown - try catch swift 4 example
¿Cómo proporcionar una descripción localizada con un tipo de error en Swift? (5)
Ahora hay dos protocolos de adopción de errores que su tipo de error puede adoptar para proporcionar información adicional a Objective-C: LocalizedError y CustomNSError. Aquí hay un error de ejemplo que los adopta a ambos:
enum MyBetterError : CustomNSError, LocalizedError {
case oops
// domain
static var errorDomain : String { return "MyDomain" }
// code
var errorCode : Int { return -666 }
// userInfo
var errorUserInfo: [String : Any] { return ["Hey":"Ho"] };
// localizedDescription
var errorDescription: String? { return "This sucks" }
// localizedFailureReason
var failureReason: String? { return "Because it sucks" }
// localizedRecoverySuggestion
var recoverySuggestion: String? { return "Give up" }
}
Estoy definiendo un tipo de error personalizado con la sintaxis Swift 3 y deseo proporcionar una descripción fácil de usar del error que devuelve la propiedad
localizedDescription
del objeto
Error
.
¿Cómo puedo hacerlo?
public enum MyError: Error {
case customError
var localizedDescription: String {
switch self {
case .customError:
return NSLocalizedString("A user-friendly description of the error.", comment: "My error")
}
}
}
let error: Error = MyError.customError
error.localizedDescription
// "The operation couldn’t be completed. (MyError error 0.)"
¿Hay alguna manera para que la Descripción
localizedDescription
devuelva mi descripción de error personalizada ("Una descripción fácil de usar del error")?
Tenga en cuenta que el objeto de error aquí es de tipo
Error
y no
MyError
.
Puedo, por supuesto, lanzar el objeto a MyError
(error as? MyError)?.localizedDescription
pero, ¿hay alguna manera de hacerlo funcionar sin transmitir a mi tipo de error?
Aquí hay una solución más elegante:
enum ApiError: String, LocalizedError {
case invalidCredentials = "Invalid credentials"
case noConnection = "No connection"
var localizedDescription: String { return NSLocalizedString(self.rawValue, comment: "") }
}
Como se describe en las notas de lanzamiento de Xcode 8 beta 6,
Los tipos de error definidos rápidamente pueden proporcionar descripciones de error localizadas mediante la adopción del nuevo protocolo LocalizedError.
En tu caso:
public enum MyError: Error {
case customError
}
extension MyError: LocalizedError {
public var errorDescription: String? {
switch self {
case .customError:
return NSLocalizedString("A user-friendly description of the error.", comment: "My error")
}
}
}
let error: Error = MyError.customError
print(error.localizedDescription) // A user-friendly description of the error.
Puede proporcionar aún más información si el error se convierte a
NSError
(que siempre es posible):
extension MyError : LocalizedError {
public var errorDescription: String? {
switch self {
case .customError:
return NSLocalizedString("I failed.", comment: "")
}
}
public var failureReason: String? {
switch self {
case .customError:
return NSLocalizedString("I don''t know why.", comment: "")
}
}
public var recoverySuggestion: String? {
switch self {
case .customError:
return NSLocalizedString("Switch it off and on again.", comment: "")
}
}
}
let error = MyError.customError as NSError
print(error.localizedDescription) // I failed.
print(error.localizedFailureReason) // Optional("I don/'t know why.")
print(error.localizedRecoverySuggestion) // Optional("Switch it off and on again.")
Al adoptar el protocolo
CustomNSError
, el error puede proporcionar un diccionario de información de
userInfo
(y también un
domain
y
code
).
Ejemplo:
extension MyError: CustomNSError {
public static var errorDomain: String {
return "myDomain"
}
public var errorCode: Int {
switch self {
case .customError:
return 999
}
}
public var errorUserInfo: [String : Any] {
switch self {
case .customError:
return [ "line": 13]
}
}
}
let error = MyError.customError as NSError
if let line = error.userInfo["line"] as? Int {
print("Error in line", line) // Error in line 13
}
print(error.code) // 999
print(error.domain) // myDomain
También agregaría, si su error tiene parámetros como este
enum NetworkError: LocalizedError {
case responseStatusError(status: Int, message: String)
}
Puede llamar a estos parámetros en su descripción localizada así:
extension NetworkError {
var errorDescription: String {
switch self {
case .responseStatusError(status: let status, message: let message):
return "Error with status /(status) and message /(message) was thrown"
}
}
Incluso puedes hacer esto más corto así:
extension NetworkError {
var errorDescription: String {
switch self {
case let .responseStatusError(status, message):
return "Error with status /(status) and message /(message) was thrown"
}
}
Usar una estructura puede ser una alternativa. Un poco de elegancia con localización estática:
import Foundation
struct MyError: LocalizedError, Equatable {
private var description: String!
init(description: String) {
self.description = description
}
var errorDescription: String? {
return description
}
public static func ==(lhs: MyError, rhs: MyError) -> Bool {
return lhs.description == rhs.description
}
}
extension MyError {
static let noConnection = MyError(description: NSLocalizedString("No internet connection",comment: ""))
static let requestFailed = MyError(description: NSLocalizedString("Request failed",comment: ""))
}
func throwNoConnectionError() throws {
throw MyError.noConnection
}
do {
try throwNoConnectionError()
}
catch let myError as MyError {
switch myError {
case .noConnection:
print("noConnection: /(myError.localizedDescription)")
case .requestFailed:
print("requestFailed: /(myError.localizedDescription)")
default:
print("default: /(myError.localizedDescription)")
}
}