我想在Swift中做一些我习惯在其他多种语言中做的事情:用自定义消息抛出运行时异常。例如(在Java中):
throw new RuntimeException("A custom message here")
我知道我可以抛出符合ErrorType协议的枚举类型,但我不希望必须为抛出的每种类型的错误定义枚举。理想情况下,我希望能够尽可能地模拟上面的示例。我考虑创建一个实现ErrorType协议的自定义类,但我甚至不知道该协议需要什么。想法吗?
我想在Swift中做一些我习惯在其他多种语言中做的事情:用自定义消息抛出运行时异常。例如(在Java中):
throw new RuntimeException("A custom message here")
我知道我可以抛出符合ErrorType协议的枚举类型,但我不希望必须为抛出的每种类型的错误定义枚举。理想情况下,我希望能够尽可能地模拟上面的示例。我考虑创建一个实现ErrorType协议的自定义类,但我甚至不知道该协议需要什么。想法吗?
当前回答
最简单的方法是使String符合Error:
extension String: Error {}
然后你可以抛出一个字符串:
throw "Some Error"
为了让字符串本身成为错误的localizedString,你可以扩展LocalizedError:
extension String: LocalizedError {
public var errorDescription: String? { return self }
}
其他回答
首先,让我们看看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 enum中)。
所需功能(背景)
#1首先,我想要没有复制/粘贴的消息,并且能够捕捉一组不同的错误情况,而不列出每个情况(解决方案,enum是非常独特的,没有复制/粘贴需要,每个enum可以被认为是另一组)。
其次,一些像“FileNotFound”这样的错误需要有变量上下文/细节,比如file-path(但Raw-Value enum不支持实例变量,与#1不同的是,内置enum不是解决方案)。
#3最后,我希望能够单独捕获每个情况,而不是捕获整个结构和/或类,然后在捕获内做切换,并希望避免忘记我们不处理的情况的重新抛出。
源代码(满足需求的解决方案)
只需从下面复制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)";
}
}
请注意,正如我的个人资料中提到的,在Apache 2.0许可下使用上述代码也是允许的(不需要归属)。 如果您不需要附加上下文变量的错误(或与其他方法进行比较),请参阅我的其他答案。
抛出代码应该清楚错误消息是适合显示给最终用户还是仅用于开发人员调试。为了表明描述可以显示给用户,我使用了实现LocalizedError协议的struct DisplayableError。
struct DisplayableError: Error, LocalizedError {
let errorDescription: String?
init(_ description: String) {
errorDescription = description
}
}
投掷用途:
throw DisplayableError("Out of pixie dust.")
显示用途:
let messageToDisplay = error.localizedDescription
重申一下@pj-finnegan的回答,几个人的评论,以及公认答案的脚注…
我更喜欢这里提供的其他几个答案(如果我在寻找最佳实践)。但如果我回答问题,最简单的方法是这样做(IFF你是在iOS/macOS/…)是使用桥接类型NSError。
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中提供的任何其他细节。如果您想将细节传递给任何捕捉到错误的人,还可以添加任何您想添加的内容。
我喜欢@Alexander-Borisenko的回答,但是当被捕捉为错误时,本地化的描述没有返回。看起来你需要使用LocalizedError代替:
struct RuntimeError: LocalizedError
{
let message: String
init(_ message: String)
{
self.message = message
}
public var errorDescription: String?
{
return message
}
}
更多细节请看这个答案。
最简单的解决方案,没有额外的扩展,枚举,类等:
NSException(name:NSExceptionName(rawValue: "name"), reason:"reason", userInfo:nil).raise()