Updating NSUserDefaults from Settings.bundle

Trying to access parameters from Root.plist in your Settings.bundle using NSUserDefaults?  Luckily NSUserDefaults will sync those values for you ... after the user opens the Settings preferences for your app first.  As pointed out here, among a few other spots, that certainly seems like a bug, or at least yet another expectation gap.  I know the first thing I do when I download an app is run over to the Settings to see what goodness I can configure.... Sheesh.

So to make it easier for me to not have to check, I blended some of the code from Richard Greene's post above with the example from CocoaDev to give me this nice wrapper:

+ (void)saveToUserDefaults:(NSString*)key value:(NSString*)valueString
{
	NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
 
	if (standardUserDefaults) {
		[standardUserDefaults setObject:valueString forKey:key];
		[standardUserDefaults synchronize];
	} else {
		NSLog(@"Unable to save %@ = %@ to user defaults", key, valueString);
	}
}
 
+ (NSString*)retrieveFromUserDefaults:(NSString*)key
{
	NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
	NSString *val = nil;
 
	if (standardUserDefaults) 
		val = [standardUserDefaults objectForKey:key];
 
	// TODO: / apparent Apple bug: if user hasn't opened Settings for this app yet (as if?!), then
	// the defaults haven't been copied in yet.  So do so here.  Adds another null check
	// for every retrieve, but should only trip the first time
	if (val == nil) { 
		NSLog(@"user defaults may not have been loaded from Settings.bundle ... doing that now ...");
		//Get the bundle path
		NSString *bPath = [[NSBundle mainBundle] bundlePath];
		NSString *settingsPath = [bPath stringByAppendingPathComponent:@"Settings.bundle"];
		NSString *plistFile = [settingsPath stringByAppendingPathComponent:@"Root.plist"];
 
		//Get the Preferences Array from the dictionary
		NSDictionary *settingsDictionary = [NSDictionary dictionaryWithContentsOfFile:plistFile];
		NSArray *preferencesArray = [settingsDictionary objectForKey:@"PreferenceSpecifiers"];
 
		//Loop through the array
		NSDictionary *item;
		for(item in preferencesArray)
		{
			//Get the key of the item.
			NSString *keyValue = [item objectForKey:@"Key"];
 
			//Get the default value specified in the plist file.
			id defaultValue = [item objectForKey:@"DefaultValue"];
 
			if (keyValue && defaultValue) {				
				[standardUserDefaults setObject:defaultValue forKey:keyValue];
				if ([keyValue compare:key] == NSOrderedSame)
					val = defaultValue;
			}
		}
		[standardUserDefaults synchronize];
	}
	return val;
}

Here's how I'm using it, for reference.  This block is in my root controller's viewDidLoad method.  It checks the value of the app's version number as stored in the userDefaults, but compares to a #define var.  Why?  Because apparently once you set a PSTitleValueSpecifier preference, you can't change it by editing the plist in Settings.bundle.  Ugh.

NSString *ver = [UtilityFunctions retrieveFromUserDefaults:@"version_number"];
if ([ver compare:kVersionNumber] != NSOrderedSame) {
	ver = kVersionNumber;
	[UtilityFunctions saveToUserDefaults:@"version_number" value:kVersionNumber];
}

Comments

Pingback

[...] See this article. [...]

Pingback

[...] Updating NSUserDefaults from Settings.bundle | greg haygood greghaygood.com/2009/03/09/updating-nsuserdefaults-from-settingsbundle – view page – cached Trying to access parameters from Root.plist in your Settings.bundle using NSUserDefaults? Luckily NSUserDefaults will sync those values for you ... Tweets about this link Topsy.Data.Twitter.User['tomonarishimada'] = {"location":"","photo":"http://a1.twimg.com/profile_images/518812310/5920_1078842333207_1290747434_174144_2459076_n_normal.jpg","name":"Tomonari Shimada","url":"http://twitter.com/tomonarishimada","nick":"tomonarishimada","description":"","influence":""}; tomonarishimada: “解決方法はこちら http://bit.ly/dnB69S よく出来ました。 ” 5 days ago view tweet retweet Filter tweets [...]

Post new comment

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.
  • You can enable syntax highlighting of source code with the following tags: <code>, <blockcode>, <pre>, <shell>, <c>, <drupal6>, <java>, <javascript>, <objc>, <perl>, <php>, <python>, <rails>, <ruby>, <sql>, <xmlcode>. The supported tag styles are: <foo>, [foo].

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.