Defining Custom Errors With Advanced Descriptions In Swift

March 1st, 2024

⏱ Reading Time: 5 mins

Swift provides a powerful mechanism for defining our own custom errors, allowing for better error handling and creating more informative and user-friendly errors in our applications. This capability is crucial when we aim to enhance the robustness and usability of Swift-based projects. By crafting proper custom errors, we can guide users with clarity and precision, significantly improving the overall user experience.

In this post, we’ll dive into the essentials of defining custom errors in Swift, exploring particular aspects and meeting some not so well-known, yet quite valuable APIs. Through practical examples and detailed explanations, you’ll get the knowledge that will let you write more reliable, as well as more intuitive for your users software.

Creating a custom error type

Defining custom errors requires the implementation of a custom type, and more particularly of an enumeration. This must mandatorily conform to the Error protocol for an important reason; without that conformance the cases defined in the enum won’t be catchable in a do-catch statement, which is the ultimate goal when specifying custom errors.

Let’s see an example of a hypothetical scenario where we implement a custom type containing errors regarding user-provided passwords:

The PasswordError enum demonstrated above has a mix of cases both with and without associated values. Each one represents a different kind of error that may possibly occur while a user tries to enter a password to a form. Enums with associated values hold additional information regarding the error.

The above example is enough in order to define custom errors. However, it’s only sufficient if we do not wish to also provide any kind of error descriptions. That’s a situation perfectly acceptable, as we may define errors meant to be consumed internally and textual descriptions never end up to the user, or us, to developers. If, on the other hand, error descriptions are desirable, then we have a few options to choose from in order to specify them.

Specifying basic error descriptions

The most common and usual approach to define descriptions for our custom errors is by conforming to CustomStringConvertible protocol and implementing the required description computed property. Usually, and mostly for clarity reasons, that conformance takes place to an extension of the enum that contains the custom error cases.

For our PasswordError example, we can implement the following:

Most of the times, an implementation similar to the one demonstrated here is fine so we can get textual descriptions of the errors. We can print them in the console while debugging and make conclusions about the various issues we encounter in the app making process.

However, the above is not the best choice to make when it comes to user-facing descriptions. There is a better alternative, which, as you will see next, comes with some advantages.

Providing localized error descriptions

To display user-friendly, localized messages to different languages, our custom error enum should conform to the LocalizedError protocol. Three new computed properties become available by doing so, with the first one being the errorDescription. That’s a String property, where, exactly as before, we set a description for each error case. To allow localization, make sure that each text is given as argument to the String(localized:) method.

For example:

You might correctly wonder why it’s preferable to conform to LocalizedError and implement the errorDescription property. The description property of the CustomStringConvertible protocol can lead to the same results if we just return localizable strings for each error case.

There are two good reasons on why to choose LocalizedError. Firstly, conforming to CustomStringConvertible usually aims to provide descriptions that we can use during the development process of an app, such as printing them in the console. Secondly, LocalizedError comes with a couple of additional perks that allow to supply more than one error messages, and therefore to achieve the best possible user experience if shown in the frontend.

Specifying failure reason and recovery suggestions

The two additional advantages we get from LocalizedError protocol regard two more computed properties beyond errorDescription, each of which we can use to provide additional descriptions about our custom errors. Both of them are optional, so we can use them on demand.

The first one is the failureReason, and that’s the place to specify more detailed explanation of an error’s cause. Similarly to errorDescription property, it may return a string value for each error case:

The other additional computed property is the recoverySuggestion. Its name speaks for itself, as that’s where we define descriptions about how users can fix or overcome an error:

By making use of all three computed properties of LocalizedError, we are able to create user-friendly, informative and helpful custom errors that increase user experience. Just remember what each property answers to:

  • errorDescription: What went wrong?
  • failureReason: Why?
  • recoverySuggestion: How to fix?

For instance:

Conclusion

Defining custom errors is a common task for developers, but as you have seen it’s a lot more than just implementing an enum. There are protocols to conform to and specify descriptions for custom errors, either basic or more advanced ones. I would suggest to use the description property of the CustomStringConvertible protocol in order to define error messages that you’ll use while making an app. Use the LocalizedError computed properties for localizable messages which will be presented to users. If you were not aware of the failureReason and recoverySuggestion properties discussed above, now it’s a good time to start using them as well.

Thank you for reading!

Stay Up To Date

Subscribe to my newsletter and get notifiied instantly when I post something new on SerialCoder.dev.

    We respect your privacy. Unsubscribe at any time.