Where to initialize iPhone application defaults - iphone

I just started using the preferences pane to let users customize some settings in my iOS app, and it was pretty easy to create the Settings.bundle and access the information from it. My problem is that I read in the Apple docs that the settings should be programatically initialized:
It is recommended that you register any default preference values programmatically at launch time in addition to including them in your settings bundle property lists. For newly installed applications, default preference values from the application’s settings bundle are not set until the Settings application runs. This means that if the user runs your application before running Settings, the default values specified in your settings bundle will not be available. Setting such values programmatically at launch time ensures that your application always has appropriate values. To register default values programmatically, use the registerDefaults: method of the NSUserDefaults class.
Where in the app is this initialization done, and how can I be sure that I'm not overwriting a user-supplied value? Is this handled in some method of the App Delegate?

You should register your defaults before you try to access a value stored in your NSUserDefaults.
You could do it in - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions.
Registering your defaults is fast, so there is no need to optimize this. Just do it at the launch of the app.
I store my userdefaults in a plist and register the content of this list, like this:
NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"DefaultUserDefaults" ofType:#"plist"]];
[[NSUserDefaults standardUserDefaults] registerDefaults:dictionary];
If you register your defaults like this you don't have to worry that you overwrite user supplied values.
NSUserdefaults uses "domains" where it stores it's values. If you register your defaults they are stored in the registration domain. If a User stores a value those values are stored in the application domain.
If you try to get a value from NSUserdefaults it looks if the value is present in the application domain, and if it's not there it takes the value from the registration domain.
Edit:
you would access those values (or better, the values that are stored in your nsuserdefaults, and those as a fallback if there are no user provided values) like this:
NSInteger minutesToWait = [[NSUserDefaults standardUserDefaults] integerForKey:#"MinutesToWait";
NSString *urlString = [[NSUserDefaults standardUserDefaults] stringForKey:#"DefaultURLStr"];
The plist is just another representation of a NSDictionary with keys and values. The key is the same key you use to access the userdefaults, and the value is your defaultvalue.
Pretty straight forward.
It doesn't matter how you create the dictionary. You can do it in code as well.

As #fluchtpunkt suggested, you can register the defaults using:
NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"DefaultUserDefaults" ofType:#"plist"]];
[[NSUserDefaults standardUserDefaults] registerDefaults:dictionary];
Personally, I check for each value independently in my App Delegate.
#define kSettings [NSUserDefaults]
if([kSettings boolForKey:#"firstRun"] == nil){
//
// Set up the initial settings...
//
[kSettings setBool:NO forKey:#"firstRun"];
}
I write a "reset" method and then call it on first run. I prefer doing this in code, but you theoretically could use a plist. I just feel like it's one less place to go wrong.

You can use the same method inside your app delegate that you use to setup your initial window, didFinishLaunchingWithOptions:
However, you may also need some logic inside applicationWillEnterForeground:, because potentially your user could put your app into the background, change settings inside the settings app, then resume your app and expect those changes to have been applied.

#MatthiasBauch or #Moshe's answers will most likely work for most people, but for anyone who like me had their settings in a Root.plist file (I was using the InAppSettingsKit), you have to dig a bit deeper into that particular plist file to get the actual default values of the settings (since they're not at the top level of the plist, but nested under the key PreferenceSpecifiers). Here is a link to another answer here, containing the extra code that worked for me:
https://stackoverflow.com/a/10497898/381233

Related

Is it okay to register defaults to NSUserDefaults from multiple classes in my app?

I have read and understand the way NSUserDefaults can be used to save preferences for my app to the file system. I am also aware of the need to register defaults before using them later in the app.
What I am not sure of, however, is if it is okay to register defaults from various classes in my app. For example, in the AppDelegate I want to register a preference that belongs to the entire app, but in a Theme-class I want to use (and therefore register) preferences only used for getting and setting the app's theme. More classes will have their own needs as far as user defaults go, so this applies to multiple parts of any project I work on.
Is this way of keeping the preferences with the classes they belong to the correct way of working?
Yes, you can register defaults from multiple classes. When you register a new default, it just adds that new key/value pair to the registration domain (there is only one of those for the app). I think the way you're doing it is quite reasonable.
The NSUserDefaults are related to the entire application. So, it's not important where you read/store them: the keys will be visible to any class even when they're set in another.
I personally try to use my own schemes, so related preferences could be "close".
i.e:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:themePreferences forKey:#"Theme"];
[defaults setObject:otherPreferences forKey:#"Other"];
Hope it helps.
This isn't necessary, but if you wanted to zone your preferences file by class, you could include the class name as part of the key. For example:
NSString *prefKey = [ NSString stringWithFormat: #"%s.prefName", (const char *) class_getName([ self class ]) ];
Where prefName is the name of whatever preference you're storing. That way you could use the same preference name with different classes, and not worry about them overwriting one another. But as others have said, unless you have a specific need to do this, NSUserDefaults can be called from anywhere in the application.
If you use [NSUserDefaults standardUserDefaults] that will be shared within entire application. And you can use your own defaults classes for class-specific settings.

Why are Settings default values not showing in iPhone app?

I’m loading default values into the apps Settings using a Root.plist file. The values seem to load fine, and I can see them from Settings on the device.
However, the app itself doesn’t seem to see those values, it only sees the values that have been manually typed into the device (via Settings).
The values appear the same in Settings, whether defaults or device-entered. I use the same code (obviously) for loading the values in the app, whether they are defaults or device-entered.
Anyone see what I'm missing?
Setting the default in Root.plist is quite misleading actually, it doesn't create default value. You have to register the defaults using
[NSUserDefaults standardUserDefaults] registerDefaults:aDictionaryHere];
aDictionaryHere is a NSDictionary object with the key and value you want to register as defaults, for example
[NSDictionary dictionaryWithObject:#"English" forKey:#"Language"];
This key and value will have to match whatever you set in your Root.plist

Keeping variable values when the iPhone is shut-down?

Lets say i have an application which has three text-fields, and i can type whatever i want into them, lets also assume that i have a checkbox and a button. And if the button is tapped while the checkbox is checked, the nsstring values in these textfields should get saved somehow. Lets say i power down my iPhone and restarted it, opened the app once again and wanted those values to be in their respective textfields.
How does one do this?
Is this a case for NSUserDefaults or something for Apple's own Keychain API to handle?
Cheers.
Edit: We used local declarations when setting and getting the values of the NSUserDefaults, which of course, doesnt work. It works perfectly now...
Yes you can make use of NSUserDefaults
Integrate a sqlite3 database into ur app.. save the textfields value in an array and sav it in the database. On starting of the apps just load from the database and retrieve the top most array and show the values back as output.. Hope it helps.. I am new also.. but cant think of anything else if u want to restart ur whole phone and still wants the fields to be filled.
Use NSUserDefaults for stuff that doesn't need to be stored securely and Keychain if it does.
You can use the NSUserDefaults or a PList or a database to save the content.
Example is given below:-
[[NSUserDefaults standardUserDefaults] setObject:#"Jayahari" forKey:#"userName"];
PList can be used for archiving, serilizing. Database also can be used for same purpose.

Can Settings bundle use values from application's Info.plist file?

is it possible to use values from application's Info.plist file as DefaultValue value for items in Settings.bundle? For example CFBundleVersion. I've tried entering it as ${CFBundleVersion} but it didn't work. I've also tried changing DefaultValue type but with no success. Any ideas?
The reasons behind are simple:
CFBundleVersion is known at compile
time, so I won't have to take its
value from application's mainBundle
and then apply that value to
NSUserDefaults.
Other reason is that just after
installing the app, but before
running it, Settings bundle values
are not in-sync as the code setting
NSUserDefaults did not have a
chance to execute itself... so it would be
boring to always remember that I have
to change my Settings bundle values
manually.
This is how I synchronize my app's version to use in my Settings.bundle
[defaults setObject:[[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleVersion"] forKey:#"kVersion"];
[defaults synchronize];
I place that piece of code in my didFinishLaunchingWithOptions: app delegate, so every time the app launches it makes sure that the correct version is displayed in my Settings.bundle. This takes the app version value from your info.plist

NSUserDefaults not working right

Having some trouble with NSUserDefaults here.
Here's how I'm creating it:
NSString *theCity = #"Test City";
[[NSUserDefaults standardUserDefaults] setObject:theCity forKey:#"SavedCity"];
Here's how I'm trying to retrieve it:
if ([[NSUserDefaults standardUserDefaults] objectForKey:#"SavedCity"])
{
NSLog(#"Key exists! %#",[[NSUserDefaults standardUserDefaults] objectForKey:#"SavedCity"]);
}
else {
NSLog(#"No city saved!");
}
The problem I have is that even if there IS a key for "SavedCity" (I check the pref file in the Simulator directory), it always displays "No city saved". Am I doing something wrong?
Thanks!
Two things you could try.
1) Try synchronizing the user defaults after settings the string. [[NSUserDefaults standardUserDefaults] synchronize]
2) Try retrieving the string using -stringForKey:
I ran into a similar problem myself recently. Here's what fixed it for me.
From the iOS Application Programming Guide:
It is recommended that you register any default preference values programmatically at launch time in addition to including them in your settings bundle property lists. For newly installed applications, default preference values from the application’s settings bundle are not set until the Settings application runs. This means that if the user runs your application before running Settings, the default values specified in your settings bundle will not be available. Setting such values programmatically at launch time ensures that your application always has appropriate values. To register default values programmatically, use the registerDefaults: method of the NSUserDefaults class.
What you should add is:
if ([[NSUserDefaults standardUserDefaults] objectForKey:#"SavedCity"] != nil)
Because you want to check if you've saved something.