Catch multiple errorTypes? - swift

I'm looking for a way to catch multiple types of errors in a catch. I've tried fallthrough and the comma separated style from a switch statement and neither works. The docs say nothing about catching multiple but pattern 1. It's not clear to me which of the pattern syntaxes would work here.
Error definitions (sample):
enum AppErrors {
case NotFound(objectType: String, id: Int)
case AlreadyUsed
}
Works:
do {
//...
} catch AppErrors.NotFound {
makeNewOne()
} catch AppErrors.AlreadyUsed {
makeNewOne()
} catch {
print("Unhandled error: \(error)")
}
Does not compile, is it possible to do something like this?
do {
//...
} catch AppErrors.NotFound, AppErrors.AlreadyUsed {
makeNewOne()
} catch {
print("Unhandled error: \(error)")
}

If you want to catch all AppErrors, you can use this pattern:
catch is AppErrors
If you're looking for more specific matching, it seems to quickly get ugly.
This will let us catch specific cases of AppErrors:
catch let error as AppErrors where error == .NotFound || error == .AlreadyUsed
There's also this syntax which seems to work:
catch let error as AppErrors where [.NotFound, .AlreadyUsed].contains(error)
For completeness sake, I'll also add this option, which allows us to catch errors of two different types, but it doesn't allow us to specify which case within those types:
catch let error where error is AppErrors || error is NSError
Finally, based on the fact that anything we catch will conform to the ErrorType protocol, we can clean up the second & third examples I provided with an ErrorType extension and use that in conjunction where a where clause in our catch:
extension ErrorType {
var isFooError: Bool {
guard let err = self as? AppErrors else { return false }
return err == .NotFound || err == .AlreadyUsed
}
}
And just catch it like this:
catch let error where error.isFooError

You can create a case that contains an AppErrors array:
indirect enum AppErrors: Error {
case NotFound
case AlreadyUsed
case errors([AppErrors])
}
Then, for the catch statement:
catch AppErrors.errors(let errors) where errors == [.NotFound, .AlreadyUsed]
Do note, however, that errors is an Array and the order matters when comparing with ==. An alternative is to use case errors(Set<AppErrors>), but that would require AppErrors to conform to Hashable protocol.
UPDATE: Come to think of it, it's better to use an OptionSet type:
public struct InvalidInput: OptionSet, Error {
public var rawValue: Int
public init(rawValue: Int) {
self.rawValue = rawValue
}
public static let noAccount = InvalidInput(rawValue: 1 << 0)
public static let noKey = InvalidInput(rawValue: 1 << 1)
public static let invalidKey = InvalidInput(rawValue: 1 << 2)
}
func validateInput() throws -> Void {
var invalid: InvalidInput = []
invalid.insert(.noKey)
invalid.insert(.noAccount)
if !invalid.isEmpty {
throw invalid
}
}
do {
try validateInput()
} catch [InvalidInput.noAccount, InvalidInput.noKey] as InvalidInput {
print("Account and key are both required.")
}
Link: http://www.chrisamanse.xyz/2016/12/03/catching-multiple-errors-in-swift

Multi-Pattern Catch Clauses proposal (SE-0276) is implemented in Swift 5.3
And now code snippet from the question is valid:
do {
//...
} catch AppErrors.NotFound, AppErrors.AlreadyUsed {
makeNewOne()
} catch {
print("Unhandled error: \(error)")
}
You can check updated docs.

Related

What error do I need to throw to for a specific catch block to handle + Swift error handling

I want to add code to throw an error that requires the "catch let printerErrorsecond" and the last catch block to handle an error. I have tried updating the value for the toPrinter parameter to a number of values without any luck. I also tried a number of else if statements and cannot seem to get either of the last two catch blocks to handle the errors. It seems like a case for both could be added in the PrinterError enum but after several attempts I could not resolve the issue. Any code based resolutions you can provide the better!
enum PrinterError: Error {
case outOfPaper
case noToner
case onFire
}
func send(job: Int, toPrinter printerName: String) throws -> String {
if printerName == "Never Has Toner" {
throw PrinterError.noToner
} else if printerName == "Fire" {
throw PrinterError.onFire
} else if printerName == "Empty" {
throw PrinterError.outOfPaper
}
return "Job sent"
}
do {
let printerResponse = try send(job: 1440, toPrinter: "Gutenberg")
print(printerResponse)
} catch PrinterError.onFire {
print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
print("Printer error: \(printerError).")
} catch {
print(error)
}
// Prints "Job sent"
try to implement it by this way
enum PrinterError: Error {
case outOfPaper
case noToner
case onFire
case deFault
case noHandeling
}
func send(job: Int, toPrinter printerName: String) throws -> String {
switch printerName {
case PrinterError.outOfPaper.localizedDescription:
print("outOfPaper")
return PrinterError.outOfPaper.localizedDescription
case PrinterError.noToner.localizedDescription:
print("noToner")
return PrinterError.noToner.localizedDescription
case PrinterError.onFire.localizedDescription:
print("onFire")
return PrinterError.onFire.localizedDescription
default:
print("default")
return PrinterError.deFault.localizedDescription
}
}
do {
try send(job: 100, toPrinter: "AAA")
} catch {
print(error.localizedDescription)
}

Swift error handling with multiple catch and conditions based on the error's enum associated value

Is there a way to catch errors with some condition on the catch based on the value of the associated value of the error enum?
Example:
enum Errors : Error {
case error1(str: String?) // optional associated value
case error2
case error3
}
func f() throws {
throw Errors.error1(str: nil)
}
do {
try f()
}
catch Errors.error1(let str) {
if(str != nil) {
print(str!)
}
else {
//throw the same error to be caught in the last catch
}
}
catch {
print("all other errors")
}
Yes of course!
In a catch, you can do pattern matching with the error, just like in a switch statement. You can read more about this in the Swift guide under the section "Handling errors using Do-Catch". This means you can use where:
do {
try f()
}
catch Errors.error1(let str) where str != nil{
}
catch {
print("all other errors")
}

Handle error in Swift 4.2 without downcasting error

I defined an error type called "HelloError", after multiple calls, I have to do downcast using as! and the error in do-catch block.
enum HelloError: Error {
case A
case B
}
func hello() throws {
try helloAgain()
}
func helloAgain() throws {
throw HelloError.A
}
do {
try hello()
} catch {
switch error as! HelloError {
case .A:
print("A")
case .B:
print("B")
}
}
Is it possible to catch the error without downcasting the error type? I want to write code like this:
do {
try hello()
} catch {
switch error {
case HelloError.A:
print("A")
case HelloError.B:
print("B")
}
}
Thank you.
You can do the switch with multiple catch blocks
do {
try hello()
} catch HelloError.A {
print("A")
} catch HelloError.B {
print("B")
}
More detailed information is in Swift Language Guide: Error handling

How can I catch and log all errors?

I'm using some third-party functions, and I don't know what types of errors they can produce.
I want to catch and log all such errors, but ErrorType protocol does not have any members that could provide a meaningful message or error code. What can be done to log any type of errors properly?
do {
try someFunction()
} catch WellKnownError { // handle it }
} catch let err {
let message = ... // how can I get error type and some meaningful message from err?
NSLog(message)
}
I think it's not an option...
But you can try something like this...
//----------------------------------------
// Private API
//----------------------------------------
public enum CarError : ErrorType
{
case NeverTurnOn
case OutOfFuel
case Other(String)
}
public class Car
{
public func turnOn(newState:Int) throws -> Bool
{
if newState == 1
{
throw CarError.NeverTurnOn
}
else if case 2...5 = newState
{
throw CarError.OutOfFuel
}
else if newState == 10
{
throw CarError.Other("Something is gonna wrong")
}
else if newState == 11
{
throw CarError.Other(":(")
}
return false
}
}
Error Handling
func main()
{
let car = Car()
do {
try car.turnOn(4)
} catch CarError.NeverTurnOn {
print("Turn off")
} catch let err as NSError {
print(err.description)
} catch {
print("It's madness!!!")
}
}
main()
With this test... I had this result...
//Error Domain=trycatchtest.CarError Code=1 "The operation couldn’t be completed. (trycatchtest.CarError error 1.)"
//Program ended with exit code: 0
It's not the best way to solve your problem, but a new way to understand what is happening in background of this framework.
Now, asking a favor.
Please, let me now about the final result ;)
It's doesn't matter if is a good or bad result.

Swift 2: Error handling: throw: How to pass an object with an error?

How to pass the userInfo reference object when throwing an error in Swift2?
Something like this would be good:
guard let refIDString = memberXMLElement.attributes["ref"] else {
throw OSMVectorMapDescriptionError.ElementDoesntContainRequiredAttributes(userInfo:memberXMLElement)
}
And then:
catch OSMVectorMapDescriptionError.ElementDoesntContainRequiredAttributes {
(userInfo:AnyObject) in
//do stuff
}
But errors are enums, we do can specify like here, but how to catch it?
public enum OSMVectorMapDescriptionError:ErrorType {
case ElementDoesntContainRequiredAttributes(userInfo:AnyObject)
}
Assuming that you don't need someLevel, you can define
public enum OSMVectorMapDescriptionError:ErrorType {
case ElementDoesntContainRequiredAttributes(userInfo: ...)
}
use your guard unchanged
guard let refIDString = memberXMLElement.attributes["ref"] else {
throw OSMVectorMapDescriptionError.
ElementDoesntContainRequiredAttributes(userInfo:memberXMLElement)
}
and
catch OSMVectorMapDescriptionError.
ElementDoesntContainRequiredAttributes(let userInfo) {
//do stuff using userInfo
}