Error. A localized description

I was living under a rock until very recently and believed that the Error protocol was truly empty with no requirements. While it is true that there are no requirements, it does define a localizedDescription String property, as a quick pit stop at its documentation page shows.

This property, strangely, does not find mention in the Error handling section of Apple’s Swift book.

So, how does an error defined like

enum GraveError: Error {
  case learningSwift
}

supply a localizedDescription?

To understand this, I did some digging and found two great resources – the NSError bridging evolution proposal and the NSError.swift file in the open source Foundation library that is used on non-Apple platforms.

The relevant protocol extension is:

public extension Error {
  /// Retrieve the localized description for this error.
  var localizedDescription: String {
    if let nsError = self as? NSError {
      return nsError.localizedDescription
    }

    let defaultUserInfo = _swift_Foundation_getErrorDefaultUserInfo(self)  as? [String : Any]

    return NSError(domain: _domain, code: _code, userInfo: defaultUserInfo).localizedDescription
  }
}

Before continuing, allow me to take a small detour about the late great NSError. Behind the scenes, NSError also conforms to Error (it is empty after all), and always had a localizedDescription. This description is derived from what you put in an NSError’s userInfo dictionary, specifically the value for the NSLocalizedErrorDescriptionKey.

So, if the error is an NSError object, it just returns that object’s localizedDescription.
If it is not an NSError, and just a good old Swift Error, it creates a user info dictionary, then creates an NSError out of it, and then returns its localizedDescription.

This is where the LocalizedError protocol enters the stage.

The LocalizedError protocol inherits from the empty Error protocol and adds four requirements of its own (empty defaults are provided in extensions), one of which is the well named errorDescription. In the process of creating the afore-mentioned user info dictionary, this errorDescription is used for the NSLocalizedErrorDescriptionKey, if the error happens to be a LocalizedError.

There are more details in NSError.swift, which I leave for the interested reader.

The long and short is this: if you are in possession of any error (whether an NSError or Error), you can print its localizedDescription. For your NSErrors, populate the user info dictionary correctly and correspondingly, it does not hurt to get into the habit of conforming all your Swift Errors to LocalizedErrors and providing an errorDescription, like so:

extension GraveError: LocalizedError {
  var errorDescription: String? {
    switch self {
    case .learningSwift: return "Haha, good joke."
    }
  }
}

If not, you can end up with the (not so) dreaded “The operation couldn’t be completed __lldb_expr xxxx code x” message.

Good luck making mistakes!

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply