web-dev-qa-db-fra.com

Comment fournir une description localisée avec un type d'erreur dans Swift?

Je définis un type d'erreur personnalisé avec la syntaxe Swift 3 et je souhaite fournir une description conviviale de l'erreur renvoyée par la propriété localizedDescription de l'objet Error. Comment puis-je le faire?

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.)"

Existe-t-il un moyen pour la localizedDescription de renvoyer ma description d'erreur personnalisée ("Description conviviale de l'erreur.")? Notez que l'objet d'erreur ici est de type Error et non pas MyError. Je peux, bien sûr, lancer l'objet à MyError

(error as? MyError)?.localizedDescription

mais y a-t-il un moyen de le faire fonctionner sans avoir recours à mon type d'erreur?

163
Evgenii

Comme décrit dans les notes de publication de Xcode 8 beta 6,

Les types d'erreur définis par Swift peuvent fournir des descriptions d'erreur localisées en adoptant le nouveau protocole LocalizedError.

Dans ton cas:

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.

Vous pouvez fournir encore plus d'informations si l'erreur est convertie en NSError (ce qui est toujours possible):

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.")

En adoptant le protocole CustomNSError, l'erreur peut fournir un dictionnaire userInfo (ainsi qu'un domain et code). Exemple:

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
327
Martin R

J'ajouterais aussi, si votre erreur a des paramètres comme celui-ci

enum NetworkError: LocalizedError {
  case responseStatusError(status: Int, message: String)
}

vous pouvez appeler ces paramètres dans votre description localisée comme ceci:

extension NetworkError {
  public var errorDescription: String? {
    switch self {
    case .responseStatusError(status: let status, message: let message):
      return "Error with status \(status) and message \(message) was thrown"
  }
}

Vous pouvez même faire ceci plus court comme ceci:

extension NetworkError {
  public var errorDescription: String? {
    switch self {
    case let .responseStatusError(status, message):
      return "Error with status \(status) and message \(message) was thrown"
  }
}
28
Reza Shirazian

Il existe maintenant deux protocoles adoptant une erreur que votre type d'erreur peut adopter afin de fournir des informations supplémentaires à Objective-C - LocalizedError et CustomNSError. Voici un exemple d'erreur qui les adopte tous les deux:

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" }

}
4
matt

Utiliser une structure peut être une alternative. Un peu d'élégance avec la localisation statique:

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)")
   }
}
1
Zafer Sevik

Voici une solution plus élégante:

  enum ApiError: String, LocalizedError {

    case invalidCredentials = "Invalid credentials"
    case noConnection = "No connection"

    var localizedDescription: String { return NSLocalizedString(self.rawValue, comment: "") }

  }
0
Vitaliy Gozhenko