What to Do with Localized Errors

Last week we found the easiest way to retrieve localized context from an error. This gives us three values to play with:

But how do we use these to effectively communicate an error to our users?

To figure it out, we should start by looking at the invariant — the one thing out of our power to control; the values native frameworks give to their errors. Here’s a small list:

Apple Framework Examples

Network Connection without Entitlement:

Network Timeout:

Network is Offline:

File Not Found:

Bad File URL:

File exists:

Localized Lessons

The first thing we learn is that these values are not supported equally across Apple’s libraries. While there’s always a description present, the recovery suggestion in particular is rarely used.

But we do see a pattern. Generally, the failure reason (when present) provides a short and conscience explanation of the error. Quick and to the point.

The description often restates the failure, but goes more in-depth. It might identify the particular error or some of the values involved, for example.

Finally, if the error is specific enough that a workaround can be assumed by the library author, that is written up and given in the recovery suggestion.

Hi, Alert

These usages suggest an easy mapping with the standard Alert dialog.

First we cast the incoming error as an NSError. Then we derive a title from its failure reason (or substitute a default title in case there’s no reason specified):

let bridge = myError as NSError

let title = 
  bridge.localizedFailureReason 
  ?? "An Error Occurred"

Then we take the description and the recovery suggestion (if it exists) and create a message body by joining them together with a space between:

let message = [
  bridge.localizedDescription,
  bridge.localizedRecoverySuggestion,
]
.compactMap { $0 }
.joined(separator: "\n\n")

Note that we don’t need to give a default here. The localized description of an NSError will never be empty. And if there’s no recovery suggestion, we just don’t add it.

Once we have a our title and message, we can pass them along to our alert controller:

let alert = UIAlertController(
  title: title, 
  message: message, 
  preferredStyle: .alert
)

When we present it, it’ll look something like:

Breaking with Tradition

To some long-time Cocoa developers, this may look a little strange. After all, AppKit provides a presentError(_:) method on NSWindow that uses the description as the title and only renders a message if there’s a recovery suggestion given:

Why not adopt this style ourselves? We’ll talk about this more next week when we dissect how to write a good error.