PSPreferences
Plain C on CoreFoundation types. Available from anywhere, including a constructor that runs before much else is set up.
Every function is named after the CFPreferences one it
replaces, so switching a tweak over is mostly a matter of changing
CF to PS. The one difference: the store is shared
by every user on the Mac, so where CFPreferences takes a user name and a
host name, these take only the domain.
#import "PSPreferences.h"
One key
CFPropertyListRef PSPreferencesCopyAppValue(CFStringRef key, CFStringRef domain); void PSPreferencesSetAppValue(CFStringRef key, CFPropertyListRef value, CFStringRef domain); Boolean PSPreferencesGetAppBooleanValue(CFStringRef key, CFStringRef domain, Boolean *exists); CFIndex PSPreferencesGetAppIntegerValue(CFStringRef key, CFStringRef domain, Boolean *exists);
Copy functions follow the CoreFoundation rule: you own the
result and must CFRelease it. They return NULL
when the key is not set. Passing NULL as the value removes the
key.
exists is how the typed getters tell a stored
false or 0 apart from nothing stored at all. Pass
NULL if you do not need to.
Several keys
CFDictionaryRef PSPreferencesCopyMultiple(CFArrayRef keysToFetch, CFStringRef domain);
void PSPreferencesSetMultiple(CFDictionaryRef keysToSet, CFArrayRef keysToRemove,
CFStringRef domain);
CFArrayRef PSPreferencesCopyKeyList(CFStringRef domain);
Pass NULL as keysToFetch to get every key in the
domain. PSPreferencesSetMultiple sets and removes in one write,
so other processes see all of it change at once.
Synchronizing
Boolean PSPreferencesAppSynchronize(CFStringRef domain);
Writes are saved as soon as they return, so you never need to call this. It
exists so a call left over from CFPreferences code does no harm.
Reacting to changes
Every write posts the notification <domain>/prefsChanged,
including the ones your settings pane makes. Listen for it to pick up a new
value without restarting:
static void changed(CFNotificationCenterRef c, void *o, CFNotificationName n,
const void *obj, CFDictionaryRef info) {
reloadSettings();
}
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, changed,
CFSTR("com.example.mytweak/prefsChanged"), NULL,
CFNotificationSuspensionBehaviorCoalesce);
Example
Boolean exists = false;
Boolean enabled = PSPreferencesGetAppBooleanValue(CFSTR("enabled"),
CFSTR("com.example.mytweak"),
&exists);
if (!exists) enabled = true; // your default
CFNumberRef n = PSPreferencesCopyAppValue(CFSTR("intensity"),
CFSTR("com.example.mytweak"));
if (n) { /* use it */ CFRelease(n); }
See also
PSUserDefaults for the same
store with typed accessors and registered defaults, which removes the
exists dance above.