Settings Bundle values not being linked correctly on iPhone - iphone

I have an issue I've been struggling with for the last few days. I have a settings bundle, root.plist, which holds some user preferences. In it are three multivalue menu items. One returns a boolean, the other two return numbers.
When I call [[NSUserDefaults standardUserDefaults] dictionaryRepresentation] and examine it in the console, only the first item in the property list shows up with its value. The other two are conspicuously absent. As a result, when I try to retrieve those values, I get zero.
What's weirder is that the settings bundle looks correct in the Settings app on the iPhone. The only thing not working is that the values are not getting passed. As far as my app is concerned, the other two multivalue menu items don't even exist.
Any suggestions are greatly appreciated.

I've used this block of code, multiple times, in applicationDidFinishLaunchingWithOptions to initialize my user defaults.
if (![[NSUserDefaults standardUserDefaults] objectForKey:#"somePropertyYouExpect"]) {
NSString *mainBundlePath = [[NSBundle mainBundle] bundlePath];
NSString *settingsPropertyListPath = [mainBundlePath
stringByAppendingPathComponent:#"Settings.bundle/Root.plist"];
NSDictionary *settingsPropertyList = [NSDictionary
dictionaryWithContentsOfFile:settingsPropertyListPath];
NSMutableArray *preferenceArray = [settingsPropertyList objectForKey:#"PreferenceSpecifiers"];
NSMutableDictionary *registerableDictionary = [NSMutableDictionary dictionary];
for (int i = 0; i < [preferenceArray count]; i++) {
NSString *key = [[preferenceArray objectAtIndex:i] objectForKey:#"Key"];
if (key) {
id value = [[preferenceArray objectAtIndex:i] objectForKey:#"DefaultValue"];
[registerableDictionary setObject:value forKey:key];
}
}
[[NSUserDefaults standardUserDefaults] registerDefaults:registerableDictionary];
[[NSUserDefaults standardUserDefaults] synchronize];
}

Make sure you are using - (void)registerDefaults:(NSDictionary *)dictionary on NSUserDefaults. Otherwise your defaults will not get pulled from the Bundle.
You have to do this early in your application, before you try to retrieve any of the default values.
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/Reference/Reference.html

Related

iphone : Settings.bundle returns null value

I was using xCode 3.2 and then moved to xCode 4.2 and getting some values from Settings.bundle ... it was working fine.
Mean while I need to edit some values in Settings.bundle but The Root.plist file was not showing so I follow the below procedure but did not make any change in file.
1) Click on the Settings.Bundle file, go over to the utilities window,
and look in the File Inspector.
2) Using the drop-down, change the file type to 'Application Bundle'
After that I could see Root.plist but now could not get its values in application. Actually getting Null instead of value.
Below is code and image of Settings.bundle
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
host = [defaults stringForKey:#"meter_preference"];
if(host == nil)
{
host = #"10.20.20.1";
DDLogError(#"Meter host is nil from NSUserDefaults, defaulting to %#", host);
}
I got the solution, just call the below code in applicationDidFinishLaunchingWithOptions to initialize User defaults. and it works
Replace somePropertyYouExpect in first line with property you stored in User Defaults.
if (![[NSUserDefaults standardUserDefaults] objectForKey:#"somePropertyYouExpect"]) {
NSString *mainBundlePath = [[NSBundle mainBundle] bundlePath];
NSString *settingsPropertyListPath = [mainBundlePath
stringByAppendingPathComponent:#"Settings.bundle/Root.plist"];
NSDictionary *settingsPropertyList = [NSDictionary
dictionaryWithContentsOfFile:settingsPropertyListPath];
NSMutableArray *preferenceArray = [settingsPropertyList objectForKey:#"PreferenceSpecifiers"];
NSMutableDictionary *registerableDictionary = [NSMutableDictionary dictionary];
for (int i = 0; i < [preferenceArray count]; i++) {
NSString *key = [[preferenceArray objectAtIndex:i] objectForKey:#"Key"];
if (key) {
id value = [[preferenceArray objectAtIndex:i] objectForKey:#"DefaultValue"];
[registerableDictionary setObject:value forKey:key];
}
}
[[NSUserDefaults standardUserDefaults] registerDefaults:registerableDictionary];
[[NSUserDefaults standardUserDefaults] synchronize];
}
From your code , try this thinks..
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
host = [defaults stringForKey:#"meter_preference"];
if(!host == nil)
{
host = #"10.20.20.1";
DDLogError(#"Meter host is nil from NSUserDefaults, defaulting to %#", host);
}
OR
Review this link may be helped you...
iPhone - reading Setting.bundle returns wrong values
NSUserDefaults Settings Bundle Plist

NSUserdefaults for multiuser

In my application, I am using a login form to enter into the application, also using NSUserDefaults to store user preferences, for example:
[storeData setObject:self.loginField.text forKey:#"USEREMAIL"];
[storeData setObject:self.PasswordField.text forKey:#"PASSWORD"];
Like I stored, if a new user logs in the NSUserDefaults stored value will be changed. But I want both preferences (ex:new userid and old userid as well as password). So please explain how to store multiple values for same key?
One way to solve this would be to store a NSDictionary with UserIDs as keys and the passwords as values.
Another option is to use Keychain as it specifically designed for this kind of thing and is also more secure.
Create a Global NSMutableArray and add above details in NSDictionary Objects and Store all Objects in array.This way you will have all user objects.You can get it whenever you want.
first of all create global array with AppDelegate class, for example..
userDefaults = [NSUserDefaults standardUserDefaults];
NSData *dataRepresentingtblArrayForSearch = [userDefaults objectForKey:#"arrScheduleDates"];
if (dataRepresentingtblArrayForSearch != nil) {
NSArray *oldSavedArray = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingtblArrayForSearch];
if (oldSavedArray != nil)
arrScheduleDates = [[NSMutableArray alloc] initWithArray:oldSavedArray];
else
arrScheduleDates = [[NSMutableArray alloc] init];
} else {
arrScheduleDates = [[NSMutableArray alloc] init];
}
[arrScheduleDates retain];
after that when you want to store the new record then get all record from arrScheduleDates array and after that add the new record and after that store like whole array like above..
i hope you understand and its helpful for you...
:)
The absolute easiest way to meet your requirements is to use the user's email address (assuming they're all unique) as the storage key for your dictionary, and the password as the value.
If you need to store more than the password, then the value of the key would be another dictionary with keys and values for the user.
An example of the simple case would look similar to :
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSDictionary *storedUsers = [userDefaults objectForKey:#"userData"];
if (nil == storedUsers) storedUsers = [NSDictionary dictionary];
NSMutableDictionary *mutableStoredUsers = [NSMutableDictionary dictionaryWithDictionary:storedUsers];
NSString *userPassword = [self.PasswordField.text copy]; //add autorelease if you aren't using ARC
NSString *userEmail = [self.loginField.text copy]; //add autorelease if you aren't using ARC
if (nil != userEmail)
{
[mutableStoredUsers setObject:userPassword forKey:userEmail];
}
[userDefaults setObject:[NSDictionary dictionaryWithDictionary:mutableStoredUsers] forKey:#"userData"];
[userDefaults synchronize];
first download and import files of given link
https://github.com/ldandersen/scifihifi-iphone/tree/master/security
then where u want to store user preferences write this code
[SFHFKeychainUtils storeUsername:loginField.text andPassword:PasswordField.text forServiceName:#"dhaya" updateExisting:YES error:&error];
where u want password write this code
NSString *password = [SFHFKeychainUtils getPasswordForUsername:loginField.text andServiceName:#"dhaya" error:&error];
NSLog(#"passwordpassword %#",password);
this will working great....

how to check nsuserdefaults is empty or not

i am getting a list of items from sqlite database and place them in an array.
i need to get this array first time my app runs.
for this i am saving this array into NSuserdafaults.
my code is like this
if ([[NSUserDefaults standardUserDefaults] arrayForKey:#"categories"]) {
[[NSUserDefaults standardUserDefaults] setObject:appdelegate.categoriesList forKey:#"categories"];
NSLog(#"categorylist>>>>>> %#",appdelegate.categoriesList);
}
categoriesArray = [[NSMutableArray alloc]init];
categoriesArray = [[[NSUserDefaults standardUserDefaults] arrayForKey:#"categories"] mutableCopy];
then it not entered into if condition.
and if i try with not equal like this.
if (![[NSUserDefaults standardUserDefaults] arrayForKey:#"categories"]) {
[[NSUserDefaults standardUserDefaults] setObject:appdelegate.categoriesList forKey:#"categories"];
NSLog(#"categorylist>>>>>> %#",appdelegate.categoriesList);
}
categoriesArray = [[NSMutableArray alloc]init];
categoriesArray = [[[NSUserDefaults standardUserDefaults] arrayForKey:#"categories"] mutableCopy];
then enter for every build and run.
But i need this for first build and run i mean at installing time.
can any one please help me.
Thank u in advance.
(let me add comment if any one did n't understand my question)
categoriesArray = [[NSMutableArray alloc]init];
categoriesArray = [[[NSUserDefaults standardUserDefaults] arrayForKey:#"categories"] mutableCopy];
The lines above create a leak: you first allocate one array, and then you immediately assign a different array to the same pointer that was keeping track of the array that you allocated. After the second line, you have no way to release the first array.
As for the main question, I suspect that the check
if (![[NSUserDefaults standardUserDefaults] arrayForKey:#"categories"]) {
succeeds every time because there's never a value written for the key #"categories". I see that the lines inside that conditional block try to write a value, but appdelegate.categoriesList may well be nil.

How to make a dump of the current NSUserDefaults standardUserDefaults state to disk and read back in?

I want to have some way of backing up the user defaults to a property list or XML, or some other appropriate file format that can be transfered over the net. How could I get a backup of these so that I can send them to a webserver and retrieve them back to the device and read them in to the user defaults database?
You can get a JSON string of the user defaults like this :
// You will need this at the top of your file
#import "CJSONSerializer.h"
// Get a dictionary of the user defaults
NSDictionary *dict = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation];
// Convert them to JSON
NSString *json = [[CJSONSerializer serializer] serializeObject:dictionary];
and to read them back into the device you can just do the opposite :
// You will need this at the top of your file
#import "CJSONDeserializer.h"
// Get the data from the server and re-create the dictionary from it
NSData *jsonData = <YOUR DATA FROM THE SERVER>;
NSDictionary *dict = [[CJSONDeserializer deserializer] deserializeAsDictionary:jsonData error:nil];
// Put each key into NSUserDefaults
for (id key in [dict allKeys]) {
id object = [dict objectforKey:key];
[NSUserDefaults standardUserDefaults] setObject:object forKey:key];
}
[[NSUserDefaults standardUserDefaults] synchronize];
Have a look at the TouchJSON project page for more details and the download link.
Hope that helps.
NB There's no error checking in the above code - you might run into problems if your JSON contains int / float / etc because setObject:forKey: will fail.
I'd suggest either XML or JSON. Both have pretty good frameworks that ease working with them (TouchXML and TouchJSON).

NSUserDefaults not present on first run on simulator

I've got some settings saved in my Settings.bundle, and I try to use them in application:didFinishLaunchingWithOptions, but on a first run on the simulator accessing objects by key always returns nil (or 0 in the case of ints). Once I go to the settings screen and then exit, they work fine for every run thereafter.
What's going on? Isn't the point of using default values in the Settings.bundle to be able to use them without requiring the user to enter them first?
If I got your question right, in your app delegate's - (void)applicationDidFinishLaunching:(UIApplication *)application, set the default values for your settings by calling
registerDefaults:dictionaryWithYourDefaultValues
on [NSUserDefaults standardUserDefaults]
- (void)applicationDidFinishLaunching:(UIApplication *)application {
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:3], #"SomeSettingKey",
#"Some string value", #"SomeOtherSettingKey",
nil];
[ud registerDefaults:dict];
}
These values will only by used if those settings haven't been set or changed by previous executions of your application.
As coneybeare said "You should detect if it is the first load, then store all your defaults initially."
On applicationDidFinishLaunching try to set default value in your preference.
Here is the sample:
NSUserDefaults *defaults =[NSUserDefaults standardUserDefaults];
if([defaults objectForKey:#"YOUR_KEY"] == nil)
{
[defaults setValue:#"KEY_VALUE" forKey:#"YOUR_KEY"];
}
When application will run second time it will come with KEY_VALUE for YOUR_KEY.
Thanks,
Jim.
Isn't the point of using default
values in the Settings.bundle to be
able to use them without requiring the
user to enter them first?
No. The point of the settings bundle is to give the user a place to edit all 3rd Party app settings in a convenient place. Whether or not this centralization is really a good idea is a User Experience issue that is off topic.
To answer your question, you should detect if it is the first load, then store all your defaults initially.
And while we are on the subject, I would also check out In App Settings Kit as it provides your app with a simple way to display your app settings in both places (in-app and Settings.app) with minimal code.
The values in the Settings.bundle are intended for the Settings app to able to fill in default values for your app. They are not used by your own app.
But you can set defaults yourself with the registerDefaults: method of NSUserDefaults. This will not actually set them on disk but just give "defaults for the defaults": they are used when no value has been set by the user yet.
Setting registerDefaults: must be done before any use of the default values. The "applicationDidFinishLaunching:" method that others suggested for this, is too late in most cases. By the time "applicationDidFinishLaunching:" is called, your views have already been loaded from the nib files, and their "viewDidLoad:" methods have been called. And they may typically read user defaults.
To guarantee that the defaults are set before first use, I use the following utility class, which loads the values from the Root.plist file and sets them with "registerDefaults:". You use this class to read user defaults instead of "[NSUserDefaults standardUserDefaults]". Use "[Settings get]" instead.
As a bonus, it also contains a registration method for user default change notifications, because I always forget how that is done.
#import "Settings.h"
#implementation Settings
static bool initialized = NO;
+ (void) setDefaults
{
NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
NSString *settingsBundlePath = [bundlePath stringByAppendingPathComponent:#"Settings.bundle"];
NSBundle *settingsBundle = [NSBundle bundleWithPath:settingsBundlePath];
NSString *settingsPath = [settingsBundle pathForResource:#"Root" ofType:#"plist"];
NSDictionary *settingsDict = [NSDictionary dictionaryWithContentsOfFile:settingsPath];
NSArray *prefSpecifierArray = [settingsDict objectForKey:#"PreferenceSpecifiers"];
NSMutableDictionary *appDefaults = [[NSMutableDictionary alloc] init];
for (NSDictionary *prefItem in prefSpecifierArray)
{
NSString *key = [prefItem objectForKey:#"Key"];
if (key != nil) {
id defaultValue = [prefItem objectForKey:#"DefaultValue"];
[appDefaults setObject:defaultValue forKey:key];
}
}
// set them in the standard user defaults
[[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults];
if (![[NSUserDefaults standardUserDefaults] synchronize]) {
NSLog(#"Settings setDefaults: Unsuccessful in writing the default settings");
}
}
+ (NSUserDefaults *)get
{
if (!initialized) {
[Settings setDefaults];
initialized = YES;
}
return [NSUserDefaults standardUserDefaults];
}
+ (void) registerForChange:(id)observer selector:(SEL)method
{
[[NSNotificationCenter defaultCenter] addObserver:observer selector:method name:NSUserDefaultsDidChangeNotification object:nil];
}
+ (void) unregisterForChange:(id)observer
{
[[NSNotificationCenter defaultCenter] removeObserver:observer name:NSUserDefaultsDidChangeNotification object:nil];
}