I want to do something in Swift 2 that I'm used to doing in multiple other languages: throw a runtime exception with a custom message. For example (in Java):
throw new RuntimeException("A custom message here")
I understand that I can throw enum types that conform to the ErrorType protocol, but I don't want to have to define enums for every type of error I throw. Ideally, I'd like to be able mimic the example above as closely as possible. I looked into creating a custom class that implements the ErrorType protocol, but I can't even figure out that what that protocol requires (see documentation). Ideas?
Swift 4:
As per:
https://developer.apple.com/documentation/foundation/nserror
if you don't want to define a custom exception, you could use a standard NSError object as follows:
import Foundation
do {
throw NSError(domain: "my error domain", 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)")
}
}
Prints:
Caught NSError: The operation could not be completed, my error domain, 42
User info:
key=ui1, value=12
key=ui2, value=val2
This allows you to provide a custom string (the error domain), plus a numeric code and a dictionary with all the additional data you need, of any type.
N.B.: this was tested on OS=Linux (Ubuntu 16.04 LTS).
Check this cool version out. The idea is to implement both String and ErrorType protocols and use the error's 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."
}
Usage:
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
}
Throwing code should make clear whether the error message is appropriate for display to end users or is only intended for developer debugging. To indicate a description is displayable to the user, I use a struct DisplayableError
that implements the LocalizedError
protocol.
struct DisplayableError: Error, LocalizedError {
let errorDescription: String?
init(_ description: String) {
errorDescription = description
}
}
Usage for throwing:
throw DisplayableError("Out of pixie dust.")
Usage for display:
let messageToDisplay = error.localizedDescription
The simplest way is to make String
conform to Error
:
extension String: Error {}
Then you can just throw a string:
throw "Some Error"
To make the string itself be the localizedString
of the error you can instead extend LocalizedError
:
extension String: LocalizedError {
public var errorDescription: String? { return self }
}
In case you don't need to catch the error and you want to immediately stop the application you can use a fatalError:
fatalError ("Custom message here")
Based on @Nick keets answer, here is a more complete example:
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
}
Originally published on my swift blog: http://eon.codes/blog/2017/09/01/throwing-simple-errors/
I like @Alexander-Borisenko's answer, but the localized description was not returned when caught as an Error. It seems that you need to use LocalizedError instead:
struct RuntimeError: LocalizedError
{
let message: String
init(_ message: String)
{
self.message = message
}
public var errorDescription: String?
{
return message
}
}
See this answer for more details.
Simplest solution without extra extensions, enums, classes and etc.:
NSException(name:NSExceptionName(rawValue: "name"), reason:"reason", userInfo:nil).raise()
@nick-keets's solution is most elegant, but it did break down for me in test target with the following compile time error:
Redundant conformance of 'String' to protocol 'Error'
Here's another approach:
struct RuntimeError: Error {
let message: String
init(_ message: String) {
self.message = message
}
public var localizedDescription: String {
return message
}
}
And to use:
throw RuntimeError("Error message.")
Source: Stackoverflow.com