我想在Swift 2中做一些我曾经用多种其他语言做的事情:用自定义消息抛出运行时异常。例如(在Java中):
throw new RuntimeException("A custom message here")
我知道我可以抛出符合ErrorType协议的枚举类型,但我不想为我抛出的每种类型的错误定义枚举。理想情况下,我希望能够尽可能地模仿上面的例子。我研究了创建一个实现ErrorType协议的自定义类,但我甚至无法弄清楚该协议需要什么(参见documentation)。想法?
答案 0 :(得分:160)
最简单的方法可能是定义一个自定义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
,您可以创建一个工厂方法来创建并使用自定义消息抛出一个。
答案 1 :(得分:114)
最简单的方法是让String
符合Error
:
extension String: Error {}
然后你可以抛出一个字符串:
throw "Some Error"
要使字符串本身成为错误的localizedString
,您可以改为LocalizedError
:
extension String: LocalizedError {
public var errorDescription: String? { return self }
}
答案 2 :(得分:15)
@ nick-keets的解决方案是最优雅的,但它确实在我的测试目标中打破了以下编译时错误:
<android.support.design.widget.CoordinatorLayout
android:id="@+id/coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="#ececec">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#f7f7f7"
android:fitsSystemWindows="true"
android:minHeight="1dp"
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed|enterAlwaysCollapsed">
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="@color/color_3"
android:layout_margin="10dp"/>
<!--<include layout="@layout/wentixiangqing_dingbu_wentidaan"/>-->
</android.support.design.widget.CollapsingToolbarLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#FFF">
<View
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="star:123"
android:textColor="#797979"/>
<View
android:layout_width="30dp"
android:layout_height="3dp"
android:layout_gravity="bottom|center_horizontal"
android:background="#F00"/>
</FrameLayout>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="30dp"
android:layout_marginRight="20dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="comment:90"
android:textColor="#797979"/>
<View
android:layout_width="30dp"
android:layout_height="3dp"
android:layout_gravity="bottom|center_horizontal"
android:background="#F00"/>
</FrameLayout>
</LinearLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="40dp"
android:layout_marginTop="1dp"
android:overScrollMode="always" />
</android.support.v4.widget.NestedScrollView>
<Button
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_gravity="bottom|end"
android:text="Comment" /></android.support.design.widget.CoordinatorLayout>
这是另一种方法:
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
}
}
答案 3 :(得分:13)
检查这个很酷的版本。我们的想法是实现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
}
答案 4 :(得分:12)
Swift 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
这允许您提供自定义字符串,以及包含任何类型所需的所有其他数据的数字代码和字典。
N.B。:这是在OS = Linux(Ubuntu 16.04 LTS)上测试的。
答案 5 :(得分:4)
基于@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/
答案 6 :(得分:4)
最简单的解决方案,没有额外的扩展,枚举,类等等:
NSException(name:NSExceptionName(rawValue: "name"), reason:"reason", userInfo:nil).raise()
答案 7 :(得分:2)
我喜欢@ Alexander-Borisenko的答案,但是当被捕获为错误时,不会返回本地化的描述。看来您需要改用LocalizedError:
struct RuntimeError: LocalizedError
{
let message: String
init(_ message: String)
{
self.message = message
}
public var errorDescription: String?
{
return message
}
}
有关更多详细信息,请参见this answer。
答案 8 :(得分:1)
首先,让我们看几个例子,然后看看如何实现。
do {
throw MyError.Failure
} catch {
print(error.localizedDescription)
}
或更具体的风格:
do {
try somethingThatThrows()
} catch MyError.Failure {
// Handle special case here.
} catch MyError.Rejected {
// Another special case...
} catch {
print(error.localizedDescription)
}
此外,分类是可能的:
do {
// ...
} catch is MyOtherErrorEnum {
// If you handle entire category equally.
} catch let error as MyError {
// Or handle few cases equally (without string-compare).
switch error {
case .Failure:
case .Rejected:
myShowErrorDialog(error);
default:
break
}
}
public enum MyError: String, LocalizedError {
case Failure = "Connection fail - double check internet access."
case Rejected = "Invalid credentials, try again."
case Unknown = "Unexpected REST-API error."
public var errorDescription: String? { self.rawValue }
}
Swift 自动定义了 error
变量,处理程序只需要读取 localizedDescription
属性。
但这很模糊,我们应该改用 catch MyError.Failure {}
样式(以明确我们处理的情况),尽管如用法示例所示,分类是可能的。
Teodor-Ciuraru's almost equal answer 仍然需要长时间的手动转换(如 catch let error as User.UserValidationError { ... }
)。
accepted categorization-enum approach 的缺点:
String
消息!? (只是为了知道确切的错误)。catch MyError.runtimeError(let errorMessage) { ... }
。NSException approach 具有与分类枚举方法相同的缺点(除了可能较短的捕获段落),而且,即使放入工厂方法来创建和抛出,也非常复杂。
这完善了现有的解决方案,使用 LocalizedError
而不是 Error
,并希望避免有人像我一样阅读所有其他帖子。
(我的懒惰让我做了很多工作。)
import Foundation
import XCTest
@testable import MyApp
class MyErrorTest: XCTestCase {
func testErrorDescription_beSameAfterThrow() {
let obj = MyError.Rejected;
let msg = "Invalid credentials, try again."
XCTAssertEqual(obj.rawValue, msg);
XCTAssertEqual(obj.localizedDescription, msg);
do {
throw obj;
} catch {
XCTAssertEqual(error.localizedDescription, msg);
}
}
func testThrow_triggersCorrectCatch() {
// Specific.
var caught = "None"
do {
throw MyError.Rejected;
} catch MyError.Failure {
caught = "Failure"
} catch MyError.Rejected {
caught = "Successful reject"
} catch {
caught = "Default"
}
XCTAssertEqual(caught, "Successful reject");
}
}
答案 9 :(得分:0)
只需使用致命错误:
fatalError ("Custom message here")
答案 10 :(得分:0)
投掷代码应该清楚该错误消息是适合最终用户显示还是仅用于开发人员调试。为了表明描述可以向用户显示,我使用实现DisplayableError
协议的结构LocalizedError
。
struct DisplayableError: Error, LocalizedError {
let errorDescription: String?
init(_ description: String) {
errorDescription = description
}
}
投掷的用途:
throw DisplayableError("Out of pixie dust.")
显示用途:
let messageToDisplay = error.localizedDescription