Diving even deeper into preferences

Sometimes we can learn from the mistakes that other people make; other times we have to make the same mistakes in order to understand the mistakes of others.

The mistake in this case is creating a preference file (a property list) which can cause an app to crash when it starts up, or unexpectedly quit when doing something mundane like opening a document which it has opened many times in the past. In my search to account for this not infrequent problem, I have already considered the common explanation that the preferences have become corrupted in some way, and that their permissions have become altered. But it’s far far easier – and quite probably much more common – for this to happen because of subtle programming errors.

To understand how this can happen, you need to understand how apps read and write settings in a preference file.

All apps have simple settings, such as whether a checkbox is checked, which are easy to write and to read. They are also fixed in the app. Imagine for a moment that the app in question has a checkbox which sets whether to use metric or non-metric units. To read that in Swift 3.1, the code might look like
let metricPrefs = defaults.bool(forKey: "metricOn")
and to write it to the preferences file might look like
defaults.setValue(metricPrefs, forKey: "metricOn")

In the preferences file, there will be an entry such as

which will set it to false, i.e. to use non-metric units.

If the preferences file contains a boolean (true/false) value for the key metricOn, then that value will be returned to the app, and it can set the state of the checkbox. If no boolean value is associated with that key, false is returned, so the app will default to using non-metric units.

Similar considerations result for most of the simple settings, for integers, floating point numbers, and strings of text.

However, many apps have much more complex settings to store, which change not only in value, but in their complexity. Take the example of a popup menu, which consists of a series of strings “one”, “two”, etc. As we use the app, not only can the contents of each string change, but the number of strings in the series (array) will change.

This broadens the potential for programmer error very considerably. One obvious problem arises when the app also has a setting to indicate which of those menu items is set, represented as an integer index into that series of strings. What happens if the number of strings falls, say to 4, but the index is left at a higher value, say 6. When the app tries to create that popup menu, it puts four items into it, and then tries to select the sixth in that list of four.

If the preferences file doesn’t even contain an array of strings for that key, there is no simple result that can be returned either. So instead of the app’s code getting the array of strings which it expects, it might not get an array at all, or the array may not contain anything (or any strings).

Writing code which handles preference files robustly is easy when you are dealing with fixed types and numbers of data objects, of simple type. The more dynamic the data, and the more complex its type and content, the greater the scope for bugs. I know this because I have spent an hour trying to stop my app from crashing on startup, simply because of some subtle problems in reading its preference file.

You can look at the preferences file, and it seems fine. Even if you know exactly what each key and value in the file should look like, it may still appear fine. Its permissions are fine too. Yet every time that you start the app, or maybe when you open an existing document, or create a new one, the app just gets blown away.

In these circumstances, tools which claim to be able to check the validity of preference files will see nothing wrong either. Repairing permissions on your Home folder, or anywhere else for that matter, will achieve nothing.

The only solution in those circumstances is then to locate the preference file and remove it – the traditional remedy which so often works.