Unsticking stuck preference settings

Preference settings are some of the most complex features of macOS. Each time that you think you’ve got the hang of them, something comes along to prove that you don’t.

When you first come across all the Property Lists stored in ~/Library/Preferences, you think that you can just edit them to change settings. You then discover that, more often than not, changing them directly does nothing, as they’re being managed by a service cfprefsd which happily overwrites your changes. Even trashing a preferences file may not help, as cfprefsd may decide to write out another copy to defeat you.

You then turn to changing preferences using the command tool defaults, which may look simple but requires you to know exactly which setting you want to change, and gives you no clues. Or you may prefer to use one of the ‘safe’ preference editors which works properly through cfprefsd. When you discover that doesn’t always work, you may think it’s the result of incorrect permissions on the Property List. When that looks to be fine, you draw a blank, and accept that you’re just supposed to leave that setting as it is.

That isn’t the right time to admit defeat, though. What you probably never even dreamed is that preferences have an overriding hierarchy, and what may have been happening is that the preference you tried to set was already being overridden by a Property List with a higher priority. If you can discover how to undo that, then you should still be able to change the setting that’s been driving you crazy.

Apple explains this in another of its invaluable documents which it has ‘retired’ without replacement: Preferences Programming Topics for Core Foundation. Preference Domains, one of the least understood parts of the whole system, are detailed here. In practice, this means there may be another Property List in ~/Library/Preferences/ByHost which is overriding that in ~/Library/Preferences. The property lists in that ByHost folder are named differently, such as com.apple.loginwindow.[UUID].plist. The UUID used corresponds to that of your Mac’s Hardware UUID, which can be found at the top level of System Information. You’ll probably find a load of old files there, whose UUIDs correspond to earlier Macs from which that Mac has copied across during Migration.

At its most long-winded, the defaults command can read something like
defaults -currentHost read -domain com.name.product keyname
where com.name.product is the app ID and keyname is the name of the key in its settings. Instead of specifying the -currentHost, you can omit that, or specify a -host hostname. Try this with a couple of global settings using -globalDomain and you’ll see what a difference it can make.

For example, on my Mac
defaults -currentHost read -globalDomain com.apple.trackpad.enableSecondaryClick
returns 1, as Trackpad Secondary Click is enabled, but
defaults read -globalDomain com.apple.trackpad.enableSecondaryClick
returns a missing key, value pair. This is because that global setting is stored in a preference file in the ByHosts folder, in this case the hidden .GlobalPreferences.[UUID].plist.

defaults -currentHost read -globalDomain NSQuitAlwaysKeepsWindows
returns a missing key, value pair, whereas
defaults read -globalDomain NSQuitAlwaysKeepsWindows
returns 1, as set in my General pane and stored in the hidden .GlobalPreferences.plist in my normal ~/Library/Preferences folder.

When you include -currentHost in a defaults command, it should make that setting in one of those property lists in the ByHosts folder, and won’t then be altered by commands which omit -currentHost, as those settings overriding those in the normal ~/Library/Preferences folder.

When you come across a setting which appears to be stuck, your first task is to identify its domain and the name of the key for that setting. One extensive listing of these is generated by writing all current settings to a text file using a command such as
defaults read > currentDefaults.text
defaults -currentHost read > currentDefaultsByHost.text

The first gives you megabytes of different settings across regular preferences, and the second is a much shorter listing of those specified in the files in the ByHost folder which override them. Be very careful how you store those dumps, as those settings contain all sorts of personal and sensitive information including software serial numbers.

Overriding in ByHost can catch the most experienced out too. This article was inspired by a recent bug which Jeff Johnson of Lapcat Software reported here. He had provided a demonstration of a privacy protection bypass in which he had inadvertently changed the NSQuitAlwaysKeepsWindows setting for -currentHost. Because that overrode settings in the General pane, users couldn’t fix that setting it in the pane afterwards. The solution is to use
defaults -currentHost delete -globalDomain NSQuitAlwaysKeepsWindows
which simply removes the overriding setting, leaving it to that in the regular preferences file to make that call.

If you have got a stuck preference setting, once you’ve ensured that it isn’t the result of incorrect permissions on that preference file, you need to discover the key name for that preference, whether it’s global or from an app-specific domain, then use defaults to look for an overriding setting with the -currentHost option. If you find that, use defaults -currentHost delete to remove that override, and your normal setting should start working again.

At least until you discover the next hidden feature about preference settings, which I haven’t discovered yet.