First, let's see few examples, then how to make that work.
Usage
do {
throw MyError.Failure
} catch {
print(error.localizedDescription)
}
Or more specific style:
do {
try somethingThatThrows()
} catch MyError.Failure {
// Handle special case here.
} catch MyError.Rejected {
// Another special case...
} catch {
print(error.localizedDescription)
}
Also, categorization is possible:
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
}
}
Definition
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 }
}
Pros and Cons
Swift defines error variable automatically, and a handler only needs to read localizedDescription property.
But that is vague, and we should use catch MyError.Failure {} style instead (to be clear about what-case we handle), although, categorization is possible as shown in usage example.
Teodor-Ciuraru's almost equal answer still needs a long manual cast (like catch let error as User.UserValidationError { ... }).
The accepted categorization-enum approach disadvantages:
- Is too vague as he comments himself, so that catchers may need to compare
String message!? (just to know exact error).
- For throwing same more than once, needs copy/pasting message!!
- Also, needs a long phrase as well, like
catch MyError.runtimeError(let errorMessage) { ... }.
The NSException approach has same disadvantages of categorization-enum approach (except maybe shorter catching paragraph), also, even if put in a factory method to create and throw, is quite complicated.
Conclusion
This completes existing solutions, by using LocalizedError instead of Error, and hopefully saves someone from reading all other posts like me.
(my laziness causes me a lot of work.)
Testing
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");
}
}