Swift: The quiet affair of the failable initializer and the nil coalescing operator


It seems strange to me that no mention of "failable" initializers is made on the page of the Swift documentation detailing the nil coalescing operator, and that equally there is nothing about nil coalescing on the failable initializer page.

Nor does the Apple blog post that introduced failable initializers link them with the nil coalescing operator. And yet, the nil coalescing operator makes working with failable initializers a simple affair and appears well matched to classes like UIFont, where should a bespoke request fail it is possible to have the fallback of a system font delivered through a type method.

For example consider this:
let font = UIFont(name: "Georgia", size: 18.0) ?? UIFont.systemFontOfSize(18.0)
where the initializer can fail if the font named does not exist but the type (or class) method guarantees that a font will be returned. It seems a match made in heaven and results not in an optional or implicitly unwrapped instance but a straightforward one. So why not do this all the time?

Silence is not always a virtue

Before we mount a campaign for the union of the failable initializer and the nil coalescing operator, we need to consider two types of situations (1) where you might need to know that nil has been returned and (2) where silence is a virtue. Let's take the example of selecting a font and envisage two scenarios:
  1. the user selected a font for their document on a different device, the font does not exist on the current device where the document is now being edited
  2. the user is reading an article that was downloaded to the app in JSON or XML format. The article has preferred fonts defined for it but those fonts don't exist on the device
In the first scenario, the user probably would appreciate the warning that fonts are missing and perhaps have the opportunity to rectify the problem in some way. Whereas in the second scenario it's more likely that we needn't bother the user about the fonts.

In the second scenario, if we know it's going to be the difference between Times New Roman and the system font then the typical reader won't be fussed. And the beauty of the system font is that it will be matched to the location and needs of the user's language. Besides if the requested fonts aren't available then there's very little to be done. It will only be the most discerning of readers who seeks out and downloads the correct font. While most would be fazed and even irritated by an app's request to download a font before allowing them to read an article because it was specified as the preferred font.

Adding complexity

Silent substitution can even work for a multilingual user, if he or she is reading well-composed articles with relevant language metadata. But still there are situations where an app might build a strategy for missing fonts, if fonts are known to be of importance to the content. For example for a design or typography magazine, to take an obvious example. In which case nil coalescing might either not be used or be used in combination with a test for nil.
var fontAvailable = false
if let font = UIFont(name: "Georgi", size: 18.0) {
    fontAvailable = true
}
fontAvailable
The font can then be silently downloaded if this is possible (and substitute the fallback one once available) or noisily alert the user where appropriate for their action.

Dealing with data

As soon as we break beyond the simplest of scenarios, it becomes a decision based on an app by app basis whether to combine the nil coalescing operator with the failable initializer or whether to make more use of nil. And even if the nil coalescing and failable combo is used it is likely that the font names at the very least will need to be interrogated to know whether it is italic or bold fonts that are being substituted, because we won't want to simply replace every unavailable font with the vanilla system one.

But even with these considerations, fonts are still an example where nil coalescing might work well. However, think of a scenario where the data or string for an article doesn't load. Do we silently ignore it and supply instead an empty page or do we use nil as a call to action and save the user the time and effort of navigating to (and away from) empty pages by letting them know the content is unavailable? The latter seems the obvious choice here. But this runs contra to the actions we might take with UIFont. And so it becomes important to note that there is no blanket decision to be made about failable initializers and the nil coalescing operator. This is because the way in which failable initializers are dealt with depends on two things: type and scenario.

If we know in advance that an article is corrupted or unavailable and will return nil, then there will be no need to navigate away from the current view when the user selects it from a table. But if we navigate to an empty page, and if this happens more than once, the user will start to become frustrated.

We might even take the occurrence of one missing article to start working through the list of others that have been presented to the user in a table to test whether they are available at all. In the hope that this will further reduce user frustration.

Conclusion

Sometimes optionals, nil and failability are a bore it must be admitted, but this doesn't mean we should always choose the easy way out. But having written this, at times the easy way out is the best and most valid route. And so it becomes that failable initializers must always be approached with an open mind and a strategy built to accommodate them.

Note: Thanks to Richard Turton (@richturton) for his generosity in pointing out an error in this post (one that was repeated many times).

Endorse on Coderwall

Comments