在Swift 2中使用自定义消息引发错误/异常的最简单方法是?


136

我想在Swift 2中做一些我曾经用其他多种语言做过的事情:使用自定义消息抛出运行​​时异常。例如(在Java中):

throw new RuntimeException("A custom message here")

我知道我可以抛出符合ErrorType协议的枚举类型,但我不想为我抛出的每种错误类型定义枚举。理想情况下,我希望能够尽可能地模仿以上示例。我研究过创建一个实现ErrorType协议的自定义类,但是我什至无法弄清楚该协议需要什么(请参阅文档)。有想法吗?


2
Swift 2投掷/接球也不例外。
zaph

Answers:


194

最简单的方法可能是定义一个定制的enum只有一个case是有一个String连接到它:

enum MyError: ErrorType {
    case runtimeError(String)
}

或者,从Swift 4开始:

enum MyError: Error {
    case runtimeError(String)
}

示例用法如下所示:

func someFunction() throws {
    throw MyError.runtimeError("some message")
}
do {
    try someFunction()
} catch MyError.runtimeError(let errorMessage) {
    print(errorMessage)
}

如果要使用现有Error类型,最通用的类​​型是NSError,您可以使用工厂方法来创建并使用自定义消息抛出。


嗨,我知道您已经发布此答案已经一年了,但是我想知道是否有可能深入了解String您的问题errorMessage,如果可以,我该怎么做?
Renan Camaforte '17

1
@RenanCamaforte对不起,我不明白这个问题?在String与此相关的MyError.RuntimeError(在集合的时间throw),并且你获得在访问它catch(带let errorMessage)。
阿库(Arkku)'17年

1
您被要求提供最简单的解决方案。创建自定义枚举,函数等的解决方案并不简单。我知道至少有一个办法,但我不会发布它那里,因为它是客观-C
Vyachaslav Gerchicov

3
@VyachaslavGerchicov如果您不知道在问题中也指定了Swift的更简单方法,那么这将是最简单的方法,即使您不认为在包括Objective-C的更一般的上下文中也是如此简单。(此外,此答案基本上是一个枚举的单行一次性定义,该函数及其调用是用法的示例,而不是解决方案的一部分。)
Arkku,

1
@Otar是的,但是...您正在谈论的是try!,此处未使用。如果没有某种通知,您的确无法发出潜在的投掷声try。(此外,代码的一部分是示例用法,而不是实际的解决方案。)
Arkku,

136

最简单的方法是使String符合Error

extension String: Error {}

然后就可以抛出一个字符串:

throw "Some Error"

要使字符串本身成为localizedString错误的,您可以扩展LocalizedError

extension String: LocalizedError {
    public var errorDescription: String? { return self }
}

这很聪明,但是有没有办法使它localizedDescription成为字符串本身呢?
villapossu

1
很优雅的方式!
Vitaliy Gozhenko

1
确实的优雅!但是它在测试目标中对我分解为以下消息Redundant conformance of 'String' to protocol 'Error':(
Alexander Borisenko

2
由于某种原因,这对我不起作用。说error.localizedDescription在抛出字符串后进行解析时无法完成操作。
诺亚·艾伦

1
警告:此扩展给我带来了外部库问题。这是我的例子。这对于管理错误的任何第三方库都是可能的;我会避免使String符合Error的扩展名。
布赖恩·瓦格纳

20

@ nick-keets的解决方案最优雅,但是它确实在测试目标中对我造成了以下编译时错误的分解:

Redundant conformance of 'String' to protocol 'Error'

这是另一种方法:

struct RuntimeError: Error {
    let message: String

    init(_ message: String) {
        self.message = message
    }

    public var localizedDescription: String {
        return message
    }
}

并使用:

throw RuntimeError("Error message.")

19

签出这个很酷的版本。这个想法是同时实现String和ErrorType协议,并使用错误的rawValue。

enum UserValidationError: String, Error {
  case noFirstNameProvided = "Please insert your first name."
  case noLastNameProvided = "Please insert your last name."
  case noAgeProvided = "Please insert your age."
  case noEmailProvided = "Please insert your email."
}

用法:

do {
  try User.define(firstName,
                  lastName: lastName,
                  age: age,
                  email: email,
                  gender: gender,
                  location: location,
                  phone: phone)
}
catch let error as User.UserValidationError {
  print(error.rawValue)
  return
}

这种方法似乎没有什么好处,因为您仍然需要as User.UserValidationError.rawValue。但是,如果不是执行CustomStringConvertiblevar description: String { return rawValue },这可能是获得使用枚举语法自定义说明,而不必经过有用的rawValue,你打印的每一个地方。
Arkku '18年

1
更好地实现localizedDescription方法以返回.rawValue
DanSkeel

16

斯威夫特4:

按照:

https://developer.apple.com/documentation/foundation/nserror

如果您不想定义自定义异常,则可以使用如下所示的标准NSError对象:

import Foundation

do {
  throw NSError(domain: "my error description", code: 42, userInfo: ["ui1":12, "ui2":"val2"] ) 
}
catch let error as NSError {
  print("Caught NSError: \(error.localizedDescription), \(error.domain), \(error.code)")
  let uis = error.userInfo 
  print("\tUser info:")
  for (key,value) in uis {
    print("\t\tkey=\(key), value=\(value)")
  }
}

印刷品:

Caught NSError: The operation could not be completed, my error description, 42
    User info:
        key=ui1, value=12
        key=ui2, value=val2

这样,您就可以提供自定义字符串,以及数字代码和字典,其中包括任何类型的所需的所有其他数据。

注意:这已在OS = Linux(Ubuntu 16.04 LTS)上进行了测试。


12

最简单的解决方案,无需额外的扩展,枚举,类等:

NSException(name:NSExceptionName(rawValue: "name"), reason:"reason", userInfo:nil).raise()

2
回覆。你对我的回答的意见,这是简单的只是在这个意义上,你已经有些武断决定,定义和枚举或延期一次是复杂的。因此,是的,您的答案的“设置”行为零,但代价是使每个引发的异常成为难以记住的复杂且非Swift风格(raise()而不是throw)的咒语。将您的解决方案与抛出异常的位置数相乘,throw Foo.Bar("baz")或者throw "foo"乘以抛出异常的位置数-IMO一线扩展或枚举的一次性费用比诸如此类的费用要好得多NSExceptionName
Arkku '18年

例如,@ Arkku postNotification需要2-3个参数,并且其选择器与此类似。您是否重写Notification和/或NotificationCenter在每个项目中都允许它接受较少的输入参数?
Vyachaslav Gerchicov '18年

1
不,我什至不会在自己的答案中使用该解决方案。我只是发布它来回答问题,而不是因为这是我自己要做的事情。无论如何,这是除了一点:我坚持认为,你的答案是复杂得多,以使用比任何矿场或尼克珍珠鸡的。当然,还有其他一些要考虑的要点,例如,如果扩展String以符合标准Error太令人惊讶,或者MyError枚举是否过于含糊(我个人会对两者都回答“是”,而是针对每个错误分别进行枚举,例如,throw ThisTypeOfError.thisParticularCase)。
Arkku '18年

6

基于@Nick keets答案,这是一个更完整的示例:

extension String: Error {} // Enables you to throw a string

extension String: LocalizedError { // Adds error.localizedDescription to Error instances
    public var errorDescription: String? { return self }
}

func test(color: NSColor) throws{
    if color == .red {
        throw "I don't like red"
    }else if color == .green {
        throw "I'm not into green"
    }else {
        throw "I like all other colors"
    }
}

do {
    try test(color: .green)
} catch let error where error.localizedDescription == "I don't like red"{
    Swift.print ("Error: \(error)") // "I don't like red"
}catch let error {
    Swift.print ("Other cases: Error: \(error.localizedDescription)") // I like all other colors
}

最初发布在我的快速博客上:http : //eon.codes/blog/2017/09/01/throwing-simple-errors/


1
TBH:我现在只是做throw NSError(message: "err", code: 0)
eonist

因此,您甚至不使用自己的示例?:D哦,第一个参数应该是domain,不是message,对吧?
NRitH

1
您的权利,领域。不,代码中添加了太多糖。我通常会制作许多小型框架和模块,并尽量降低便利的扩展性。这些天,我尝试使用结果和NSError之间的混合
eonist

6

如果您不需要捕获错误并且想要立即停止应用程序,则可以使用fatalError: fatalError ("Custom message here")


3
请注意,这不会引发可以捕获的错误。这将使应用程序崩溃。
阿迪尔·侯赛因

4

我喜欢@ Alexander-Borisenko的答案,但是当被捕获为错误时,不会返回本地化的描述。看来您需要改用LocalizedError:

struct RuntimeError: LocalizedError
{
    let message: String

    init(_ message: String)
    {
        self.message = message
    }

    public var errorDescription: String?
    {
        return message
    }
}

有关更多详细信息,请参见此答案

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.