PSUserDefaults
Deliberately shaped like NSUserDefaults, so it reads the way you
expect. It is a different store: values go to
/Library/TweakInject/Preferences/Defaults/, where a settings
pane in another process can see them.
#import "PSUserDefaults.h"
Creating one
- (nullable instancetype)initWithSuiteName:(NSString *)domain; @property (readonly, copy) NSString *suiteName;
There is no shared singleton and init is unavailable: every
instance names its domain, because a tweak writing to the wrong domain fails
silently and is tedious to find. Pass your package identifier.
Defaults
- (void)registerDefaults:(NSDictionary<NSString *, id> *)defaults;
Values used when a key has never been written. They are kept in memory and
never saved, so call this each time your tweak loads, before reading,
exactly as with NSUserDefaults.
Every PSUserDefaults for the same domain in a process sees
them, so you can register once and read from another instance. Unlike
NSUserDefaults, they do not carry over to other domains: several
tweaks load into the same app, and one tweak's fallbacks must not answer for
another's keys.
Typed accessors
- (BOOL)boolForKey:(NSString *)key; - (NSInteger)integerForKey:(NSString *)key; - (double)doubleForKey:(NSString *)key; - (float)floatForKey:(NSString *)key; - (nullable NSString *)stringForKey:(NSString *)key; - (nullable NSArray *)arrayForKey:(NSString *)key; - (nullable NSDictionary *)dictionaryForKey:(NSString *)key; - (nullable NSData *)dataForKey:(NSString *)key; - (nullable NSArray<NSString *> *)stringArrayForKey:(NSString *)key; - (nullable NSURL *)URLForKey:(NSString *)key; - (void)setBool:(BOOL)value forKey:(NSString *)key; - (void)setInteger:(NSInteger)value forKey:(NSString *)key; - (void)setDouble:(double)value forKey:(NSString *)key; - (void)setFloat:(float)value forKey:(NSString *)key; - (void)setURL:(nullable NSURL *)url forKey:(NSString *)key;
A URL is stored as a plain string, a path for a file URL, so the plist stays readable.
Objects and subscripting
- (nullable id)objectForKey:(NSString *)key; - (void)setObject:(nullable id)value forKey:(NSString *)key; - (void)removeObjectForKey:(NSString *)key; - (nullable id)objectForKeyedSubscript:(NSString *)key; - (void)setObject:(nullable id)value forKeyedSubscript:(NSString *)key;
The subscript pair means defaults[@"key"] works for both reading
and writing.
Everything at once
- (NSDictionary<NSString *, id> *)dictionaryRepresentation;
Every key with a value, your registered defaults included.
Reading and replacing a whole domain
- (NSDictionary<NSString *, id> *)persistentDomainForName:(NSString *)domainName; - (void)setPersistentDomain:(NSDictionary<NSString *, id> *)domain forName:(NSString *)domainName; - (void)removePersistentDomainForName:(NSString *)domainName;
These three work on a domain's whole plist at once, rather than a key at a
time. "Persistent domain" is the NSUserDefaults name for that
plist.
Values you pass to registerDefaults: are not part of it. They
are fallbacks kept in memory and never written to disk, so these methods
leave them out, while dictionaryRepresentation above includes
them.
Reading takes a domain name rather than using the instance's own, so a settings pane can read another tweak's values. Writing replaces everything in the domain in one go, and removing deletes every stored key, which leaves your registered defaults in force. That last one is how you offer a "reset to defaults" button.
// Reset to defaults [prefs removePersistentDomainForName:prefs.suiteName]; // Save and restore a preset NSDictionary *preset = [prefs persistentDomainForName:prefs.suiteName]; [prefs setPersistentDomain:preset forName:prefs.suiteName];
Reacting to changes
extern NSNotificationName const PSUserDefaultsDidChangeNotification;
Posted on the main thread, with the PSUserDefaults as its
object, whenever its domain changes, including when your settings pane
saves. Keep the instance around for as long as you want to hear about it.
[NSNotificationCenter.defaultCenter addObserverForName:PSUserDefaultsDidChangeNotification
object:prefs
queue:nil
usingBlock:^(NSNotification *note) {
reloadSettings();
}];
Synchronizing
- (BOOL)synchronize;
Writes are saved as soon as they return, so you never need to call this.
Example
PSUserDefaults *prefs =
[[PSUserDefaults alloc] initWithSuiteName:@"com.example.mytweak"];
[prefs registerDefaults:@{ @"enabled": @YES, @"intensity": @0.5 }];
if ([prefs boolForKey:@"enabled"]) {
double intensity = [prefs doubleForKey:@"intensity"];
apply(intensity);
}
prefs[@"lastRun"] = [NSDate date];