【问题标题】:How to provide a localized description with an Error type in Swift?如何在 Swift 中提供带有错误类型的本地化描述?
【发布时间】:2017-01-03 17:03:27
【问题描述】:

我正在使用 Swift 3 语法定义一个自定义错误类型,我想提供一个用户友好的错误描述,该错误由 Error 对象的 localizedDescription 属性返回。我该怎么做?

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

localizedDescription 有没有办法返回我的自定义错误描述(“错误的用户友好描述。”)?注意这里的错误对象是Error 类型而不是MyError。当然,我可以将对象转换为 MyError

(error as? MyError)?.localizedDescription

但是有没有办法让它在不转换为我的错误类型的情况下工作?

【问题讨论】:

    标签: swift swift3 error-handling nslocalizedstring


    【解决方案1】:

    如 Xcode 8 beta 6 发行说明中所述,

    Swift 定义的错误类型可以通过采用新的 LocalizedError 协议来提供本地化的错误描述。

    在你的情况下:

    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.
    

    如果错误被转换,您可以提供更多信息 到NSError(这总是可能的):

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

    通过采用CustomNSError 协议,错误可以提供 userInfo 字典(以及 domaincode)。示例:

    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
    

    【讨论】:

    【解决方案2】:

    如果你的错误有这样的参数,我还要补充一下

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

    您可以像这样在本地化描述中调用这些参数:

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

    你甚至可以像这样缩短它:

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

    【讨论】:

      【解决方案3】:

      现在有两种错误采用协议可供您的错误类型采用,以便为 Objective-C 提供额外信息——LocalizedError 和 CustomNSError。这是一个同时采用它们的示例错误:

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

      【讨论】:

      • 你能编辑一下吗?您的示例对理解每个示例的价值没有多大帮助。或者只是删除它,因为 MartinR 的回答正好提供了这个......
      【解决方案4】:

      使用结构可以是另一种选择。静态本地化有点优雅:

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

      【讨论】:

        【解决方案5】:

        这个对我有用:

        NSError(domain: "com.your", code: 0, userInfo: [NSLocalizedDescriptionKey: "Error description"])
        

        【讨论】:

          【解决方案6】:

          这是更优雅的解决方案:

            enum ApiError: String, LocalizedError {
          
              case invalidCredentials = "Invalid credentials"
              case noConnection = "No connection"
          
              var localizedDescription: String { return NSLocalizedString(self.rawValue, comment: "") }
          
            }
          

          【讨论】:

          • 这在运行时可能会更优雅,但是静态本地化步骤将无法为翻译人员提取这些字符串;当您运行 exportLocalizationsgenstrings 创建可翻译文本列表时,您会看到 "Bad entry in file – Argument is not a literal string" 错误。
          • @savinola 同意,静态本地化在这种情况下不起作用。也许使用switch + case 是唯一的选择......
          • 使用原始值也会阻止对任何错误使用关联值
          猜你喜欢
          • 2019-06-22
          • 2018-01-28
          • 1970-01-01
          • 2017-05-10
          • 2017-03-25
          • 1970-01-01
          • 2011-11-15
          • 1970-01-01
          • 2018-09-26
          相关资源
          最近更新 更多