Most apps rely on settings that persist each time you use them, whether they’re exposed to the user in a Settings dialog or just records of the size and placement of their windows. In Classic Mac OS those were often saved to the app’s resources or those of its documents. In Unix there are plain text config files that may serve similar purposes. Mac OS X inherited a novel alternative from NeXTSTEP, a human-readable property list to store serialised objects.
Mac OS X replaced the old format of those preference files with two formatting schemes, with XML the standard for app preferences. Those property lists consist of a dictionary of key-value pairs, such as
<dict>
<key>metricUnits</key>
<true/>
<key>filePrefix</key>
<string>MyFile</string>
</dict>
to set metricUnits to true and filePrefix to the string “MyFile”. At first those were stored in plain text, but by Mac OS X 10.4 they had become stored in a more efficient binary format, the binary property list, or bplist.
Defaults and their server
As these preference files became near-universal, Apple built support for them into Mac OS X, as User Defaults. With settings as preferences and now defaults, the next step was to provide a defaults server, cfprefsd, to automatically open an app’s preferences as that app started up, and make those key-value pairs available to the app when it’s running. Instead of each app having to do that for itself, macOS provides it as a service with a standard API for fetching and saving those key-value pairs.
cfprefsd is transparent to the developer, whose code simply accesses key-value pairs as they are required. cfprefsd may opt to keep the whole preference file in memory, and manages it however it sees fit. Thus the property list’s contents on disk may not represent those held in memory for the app, and any changes to the property list file may be overwritten when cfprefsd saves changed values from memory.
For a simple app, working with cfprefsd should be straightforward. The app’s preference property list is opened by cfprefsd shortly after the app is launched, and the app’s code works through UserDefaults to make any changes to key-value pairs while the app is running. As the app is shutting down, cfprefsd updates the preference file, and the user is once again free to change or delete that property list as they wish. However, there’s ample scope for that to become more complicated, or to misuse it.
Problems
Many apps today aren’t that simple in their structure, and use helper apps and other executable code that may still be running with access to the app’s preferences even though the main app is shut down. When the user thinks it’s safe to modify the contents of that property list, it may still be in the care of cfprefsd. The preferred approach then is to use the defaults command tool, which should work with cfprefsd rather than competing against it.
In the past, UserDefaults and cfprefsd weren’t always reliable, and some developers worked around their problems with a combination of the official API and performing their own direct manipulation of preference files. Those dangerous practices should have died out now.
Because an app’s preferences are accessed early as it’s being launched, any bugs or incompatibilities in those key-value pairs can have fatal effects before the app is fully open. For example, if a new version of an app reuses an existing preference key with a different data type, if it reads an old version of its preferences, that will throw an error. If that’s not handled well, that can cause the new version of the app to crash when launched.
Fortunately, all apps have to be able to create their own preference file when they’re first run. There’s scope for further bugs there, when the file created isn’t updated to work with changed key-value pairs in a newer version of the app. That may result in an app that crashes when launched even when there’s no existing preference file saved, a problem for which there’s no workaround.
Finally, many apps have multiple preference files. If they run in a sandbox, the copy they use normally is in the Data/Library/Preferences folder in their container, in ~/Library/Containers. But they may also have a different property list in ~/Library/Preferences, and sometimes a master copy in /Library/Preferences as well. While I’m sure cfprefsd knows which to access, you may need to check by inspecting each file’s timestamps.
Changing settings
There are only two safe and robust ways to change settings in preference files or user defaults: an app like Prefs Editor that works through cfprefsd, or the command tool defaults which does much the same.
If you’re certain that no app or other process might still be accessing its preferences, so they will have been safely saved to disk, you should be able to open a preferences file and edit it using a good text editor that can work with bplists, such as BBEdit, or remove the file altogether to force the app to create a new one.
defaults should be simple to use in most circumstances, although it can get complicated if you need to specify hosts, or to manipulate complex data types. Start by identifying the domain name of an app with saved preferences, which should look like a URL reversed, such as co.eclecticlight.Consolation2. This is the preferences file name, less its extension. To list all the key-value pairs for that preference file use the command
defaults read co.eclecticlight.Consolation2
What that listing doesn’t tell you is the data type of those values. Those in double quotation marks “” are strings of plain text, but numbers without quotation marks can be Booleans, integers or real numbers. If in doubt, use a command like
defaults read-type co.eclecticlight.Consolation2 textStartTime
to tell you the data type for the key textStartTime.
Check an individual key-value pair by specifying the key in a command like
defaults read co.eclecticlight.Consolation2 textStartTime
to return the value
08:16:13
which must be a string because of its contents.
To change that value, use a command like
defaults write co.eclecticlight.Consolation2 textStartTime "18:10:43"
and check that’s been written correctly by repeating the defaults read command.
To set data types other than strings you must specify the type explicitly, as in
defaults write co.eclecticlight.Consolation2 noUpdateCheck -bool "TRUE"
where you can use TRUE, FALSE, YES or NO for the new value.
The final essential defaults commands let you remove complete key-value pairs, as in
defaults delete co.eclecticlight.Consolation2 noUpdateCheck
or the whole preference file
defaults delete co.eclecticlight.Consolation2
If you make a mess of the preferences file, don’t be afraid to restore it from a recent backup, or to delete the whole thing and let the app build a new one from scratch.
Finally, these days with the threat of ClickFix malware, never copy and paste any command into Terminal without understanding exactly what it does, and checking it thoroughly before pressing Return.
Key points
- Don’t mess with preferences files carelessly.
- Use a preferences editor that works with
cfprefsd, or thedefaultscommand in Terminal. - For more simple tasks, using the
defaultscommand should be straightforward. - If a simple app isn’t running, you should be able to get away with editing or deleting its preferences file directly, if you pick the correct one.
- Never blindly paste any command into Terminal. Beware ClickFix!
Further reading
man defaults in Terminal for its documentation
UserDefaults (Apple)
Preferences and Settings Programming Guide (Apple) from 2013
Preferences: Property lists and their use, this blog
Thomas Tempelmann’s Prefs Editor works with cfprefsd
