Chris Amanse

A blog on software engineering, and iOS

Catching Multiple Errors in Swift

swift
|
Dec 3, 2016

Currently, I’m building a one-time password generator app, and for the part in validating user input, I’m using Swift’s error-handling statements to catch errors in the input. In order to write less code, I tried catching multiple errors in a catch statement:

do{tryvalidate(input)}catchInvalidInput.noAccount,InvalidInput.noKey{// No account and no key}

As it turns out, it’s not yet possible in Swift. Therefore, I tried to find “ways” to catch multiple errors in Swift.

First Solution: Recursive Enum

My first attempt to solve this is to add a case in the enum that has an associated value of an array of cases:

Now, for the catch statement, all I have to do is catch the InvalidInput.errors case:

do{tryvalidate(input)}catchInvalidInput.errors(leterrors)whereerrors==[.noAccount,.noKey]{// Handle no account, and no key case}

That’s great! However, since errors is an array, the order matters when comparing with another array. Thus, if errors is equal to [.noKey, .noAccount], the error will not be caught by the catch statement above. To fix this, we can use a Set instead of an Array. But, we still have to conform the InvalidInput enum to the Hashable protocol. That seems to complicate our code more.

Second Solution: OptionSet

My first attempt was not really a pretty solution. The reason is that I still tried to use an enum. The beauty of Swift’s error handling is that we can throw any type that conforms to the Error protocol. That means, we can use a struct instead that conforms to OptionSet.

Error messages

We can even extend the InvalidInput type to give it’s error messages:

extensionInvalidInput{varerrorMessages:[String]{letmessages=[String]()ifself.contains(.noAccount){messages.append("Account is required.")}ifself.contains(.noKey){messages.append("Key is required.")}returnmessages}}

That’s it! By using an OptionSet instead, we can have more flexible catch statements. This is especially useful for showing errors in a form. Instead of having one error message, we can inspect if the thrown error contains specific errors, and display that error in the corresponding input field.

Here’s the final code:

structInvalidInput:OptionSet,Error{publicvarrawValue:UInt8publicinit(rawValue:UInt8){self.rawValue=rawValue}staticletnoAccount=InvalidInput(rawValue:1<<0)staticletnoKey=InvalidInput(rawValue:1<<1)varerrorMessages:[String]{letmessages=[String]()ifself.contains(.noAccount){messages.append("Account is required.")}ifself.contains(.noKey){messages.append("Key is required.")}returnmessages}}