我希望在Swift中做一些我已经习惯在其他多种语言中做的事情:使用自定义消息抛出一个运行时异常。例如(在Java中):
throw new RuntimeException("A custom message here")
我知道可以抛出符合ErrorType协议的枚举类型,但我不想为每种错误类型都定义一个枚举。理想情况下,我想尽可能地模仿上面的示例。我尝试创建一个实现ErrorType 协议的自定义类,但我甚至无法确定该协议需要什么。有什么想法吗?
我希望在Swift中做一些我已经习惯在其他多种语言中做的事情:使用自定义消息抛出一个运行时异常。例如(在Java中):
throw new RuntimeException("A custom message here")
我知道可以抛出符合ErrorType协议的枚举类型,但我不想为每种错误类型都定义一个枚举。理想情况下,我想尽可能地模仿上面的示例。我尝试创建一个实现ErrorType 协议的自定义类,但我甚至无法确定该协议需要什么。有什么想法吗?
首先,让我们看一下LocalizedErrorEnum
的使用示例,然后了解如何使这些示例工作(在源代码部分)。
do {
let path = "/my/path/to/file.txt";
throw MyErrorCategory.FileNotFound(
atPath: path
);
} catch {
print(error.localizedDescription);
}
输出:
Failed to find file. {
atPath: /my/path/to/file.txt
}
定义:
public enum MyErrorCategory: LocalizedErrorEnum {
case FileNotFound(String = "Failed to find file.", atPath: String)
case Connection(String = "Connection fail - double check internet access.")
}
LocalizedErrorEnum
枚举中)。
#1 首先,我想要没有复制/粘贴的消息,并且能够捕获不同错误情况的一组错误,而无需列出每个情况(解决方案是 enum
是相当独特的,无需复制/粘贴即可使用,每个枚举可以被视为另一个组)。
#2 其次,一些错误,如“FileNotFound”,需要具有变量上下文/详细信息,例如文件路径(但 Raw-Value enum
不支持实例变量,不像 #1,内置的enum
不是解决方案)。
#3 最后,我希望能够单独捕获每种情况,而不是捕获整个 struct
和/或 class
,然后在 catch
中进行 switch
,并且希望避免忘记重新引发我们未处理的情况。
只需从下面复制并添加 LocalizedErrorEnum
到您的项目中,然后根据需要重用带有关联枚举的消息。
public protocol LocalizedErrorEnum: LocalizedError {
var errorDescription: String? { get }
}
extension LocalizedErrorEnum {
public var errorDescription: String? {
if let current = Mirror(reflecting: self).children.first {
let mirror = Mirror(reflecting: current.value);
// Initial error description.
let message = mirror.children.first?.value as? String
?? current.label ?? "Unknown-case";
var context = "";
// Iterate additional context.
var i = 0;
for associated in mirror.children {
if i >= 1 {
if let text = associated.value as? String {
context += "\n ";
if let label: String = associated.label {
context += "\(label): "
}
context += text;
}
}
i += 1;
}
return context.isEmpty ? message : (
message + " {" + context + "\n}"
);
}
return "\(self)";
}
}
抛出代码时应明确错误消息是适合显示给最终用户还是仅用于开发人员调试。为了指示描述可供用户显示,我使用实现LocalizedError
协议的结构体DisplayableError
。
struct DisplayableError: Error, LocalizedError {
let errorDescription: String?
init(_ description: String) {
errorDescription = description
}
}
使用throwing关键字:
throw DisplayableError("Out of pixie dust.")
显示用途:
let messageToDisplay = error.localizedDescription
重申@pj-finnegan的答案,几个人的评论和被接受的答案的脚注...
我更喜欢这里提供的其他几个答案(如果我在寻找最佳实践)。但是,如果我正在回答所提出的问题,最简单的方法(如果你在iOS/macOS/...中)是使用桥接类型NSError
。(这个问题标记有iOS
,所以我们可以假设它存在。)
func myFunction(meNoLikey:Bool) throws {
guard meNoLikey == false else {
throw NSError(domain: "SubsystemOfMyApp", code: 99, userInfo: [NSLocalizedDescriptionKey: "My Message!"] )
}
// safe to carry on…
}
userInfo
键 NSLocalizedDescriptionKey
是传达您请求的消息所需的唯一内容。NSError.UserInfoKey
以获取您想要在 userInfo 中提供的任何其他详细信息。如果您希望向捕获错误的任何人提供详细信息,还可以添加任何内容。我希望提出一些对已提出解决方案的变化建议:
public enum MyError: Error {
var localizedDescription: String {
get {
switch(self) {
case .network(let message, let code):
return "\(message) (\(code))"
case .invalidInput(message: let message):
return message
}
}
}
case network(message: String, code: Int)
case invalidInput(message: String)
}
这种方法需要的工作量更大,但它提供了最好的所有优点:
枚举类型
,因此可以在switch
语句中使用。String
的枚举不同)localizedDescription
下提供每个开发人员预期的消息。extension NSError {
/// Create `NSError` that is potentially compatible with `LocalizedError`.
/// Parameter string literals can be extracted and localized with Xcode build-in tools.
@available(macOS 13, iOS 16, tvOS 16, watchOS 9, *)
public convenience init(domain: String = #file, code: Int = Int(#line), description: LocalizedStringResource? = nil, failureReason: LocalizedStringResource? = nil, recoverySuggestion: LocalizedStringResource? = nil, helpAnchor: LocalizedStringResource? = nil) {
var userInfo = [String : Any]()
if let description {
userInfo[NSLocalizedDescriptionKey] = String(localized: description)
}
if let failureReason {
userInfo[NSLocalizedFailureReasonErrorKey] = String(localized: failureReason)
}
if let recoverySuggestion {
userInfo[NSLocalizedFailureReasonErrorKey] = String(localized: recoverySuggestion)
}
if let helpAnchor {
userInfo[NSHelpAnchorErrorKey] = String(localized: helpAnchor)
}
self.init(domain: domain, code: code, userInfo: userInfo.isEmpty ? nil : userInfo)
}
}
throw error = NSError(description: "Error message to localise.")
// Or in more verbose way.
throw error = NSError(description: "Error message to localise.", failureReason: "FailureReason")
extension NSError: LocalizedError {
public var errorDescription: String? { localizedDescription }
public var failureReason: String? { localizedFailureReason }
public var recoverySuggestion: String? { localizedRecoverySuggestion }
// public var helpAnchor: String? { get } // Goes as is.
}
NSLocalizedString()
中。
有一个小型SPM称为NSErrorExtension,可以使代码适用于旧版本的平台。