Unexpected results from NSUserDefaults boolForKey - iphone

After uninstalling an application completely from the device and then loading it in the debugger, I am attempting in a setup method to load a flag using boolForKey. The first time the app runs I have the expectation that the bool will not exist, since I have just reinstalled the app. I expect from the documentation that boolForKey will therefore return NO.
I am seeing the opposite though. boolForKey is returning YES, which fubars my initial user settings. Any idea why this might be happening or a good way around it?
BOOL stopAutoLogin = [[NSUserDefaults standardUserDefaults] boolForKey:#"StopAutoLogin"];
_userWantsAutoLogin = !stopAutoLogin;
So stopAutoLogin comes out as "YES", which is completely unexpected.
Stranger and stranger: When I call objectForKey:#"StopAutoLogin" I get a nil object, as expected. It's just the boolForKey that returns a bad value. So I changed the code to this:
// this is nil
NSObject *wrapper = [[NSUserDefaults standardUserDefaults] objectForKey:#"StopAutoLogin"];
// this is YES
BOOL stopAutoLogin = [[NSUserDefaults standardUserDefaults] boolForKey:#"StopAutoLogin"];

please try [UserDefaults synchronize];
Because this method is automatically invoked at periodic intervals, use this method only if you cannot wait for the automatic synchronization (for example, if your application is about to exit) or if you want to update the user defaults to what is on disk even though you have not made any changes.
please see: http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/Reference/Reference.html

Do you register the default values for your keys?
NSMutableDictionary *appDefaults = [NSMutableDictionary dictionaryWithCapacity:1];
[appDefaults setObject:#"NO" forKey:kReloadOnStartKey];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults registerDefaults:appDefaults];
If there is no registration domain,
one is created using the specified
dictionary, and NSRegistrationDomain
is added to the end of the search
list.
The contents of the registration
domain are not written to disk; you
need to call this method each time
your application starts. You can place
a plist file in the application's
Resources directory and call
registerDefaults: with the contents
that you read in from that file.
See this link for more information.

Related

Setting a preference value from stored preference

Playing with my code today,
I run this particular piece of code several times in minor variations throughout a particular class, I'm trying to streamline though. The difference in effect is minimal but changes the amount of code by a volume of hundreds or thousands of lines so would be a big personal win for me.
Essentially I have a value stored as an integer with a key of 'codeKey' and I want to insert the value of that key where the number 30061 currently resides. I'm at a bit of a loss how, can anyone help me out with this one?
I know I need to recall the value somehow and place it in but I'm not really sure how that would look.
if ([[NSUserDefaults standardUserDefaults] integerForKey:#"buttonID"] == 1) {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setInteger:30061 forKey:#"scifi1"];
[userDefaults synchronize];}
I take it you mean dynamically saving this information without duplicating the same code over and over. If that is correct, your solution will be something like this:
-(void)saveCodeKey:(int)key {
if ([[NSUserDefaults standardUserDefaults] integerForKey:#"buttonID"] == 1) {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setInteger:key forKey:#"scifi1"];
[userDefaults synchronize];
}
}
Now you can simply invoke [self saveCodeKey:12345]; Assuming the -saveCodeKey: method resides in the same class.
Hope this helps !

Appending Value to NSMutableArray and Saving to NSUserDefaults

I am creating a names generator app for iPhone and this is what I am trying to do.
Allow the user to save the current generated name by clicking a save button.
In order to do this, here is what is happening:
The current babyname is displayed in a UILabel.
The user presses 'save' and then the label.text value is appended to a NSMutableArray (I don't think this is working in my code correctly).
The NSMutableArray is then saved in NSUserDefaults.
The contents will then be loaded in another view to populate UITableView cells.
My specific question is, am I handling this saving/persistent storage process correctly? Here is the snippet in question:
- (IBAction)saveName:(id)sender {
// save current baby name to array
NSMutableArray *babyNameArray = [[NSMutableArray alloc] init];
[babyNameArray addObject:babyname.text];
// save list of babynames to nsuserdefaults
[[NSUserDefaults standardUserDefaults] setObject:babyNameArray forKey:#"My Key"];
// for testing log names listed in nsuserdefaults
NSLog(#"%#", [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]);
}
Here is a link to the pastebin of the whole file contents (with comments): http://pastebin.com/hQRM9Azh
Every time you add, you're starting again with an empty array ([[NSMutableArray alloc] init]) – instead of starting with the existing items.
Here's some code which adds to the existing entries instead:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *babyNameArray = [[defaults stringArrayForKey:#"My Key"] mutableCopy];
if (!babyNameArray) babyNameArray = [[NSMutableArray alloc] init];
[babyNameArray addObject:babyname.text];
[defaults setObject:babyNameArray forKey:#"My Key"];
[defaults synchronize];
A couple of things before we start addressing the big issue. First, you'll want to have those arrays of names stored somewhere in a file(s), which you'll read either when the application starts, or in ViewWillAppear/ViewWillLoad.
Second, the way you have things working right now, you will only save one name at a time. You alloc & init a new (empty) array every time the user clicks the "Save Name" button. You then add the current name to this (empty) array and set it as the object for key "My Key." Note that your "My Key" object will always have only one element - the most recently saved name.
And lastly, you never actually save your changes to NSUserDefaults. If I recall correctly, after you are done making changes to it, you need to call synchronize - otherwise your changes will be lost as soon as the application closes. Which kind of kills the whole purpose of using data persistance. :)

Storing values from NSUserDefaults in Settings bundle

I am trying to find out how to save/store my values from NSDefaults so that when I exit the application they are stored in the Settings.bundle. This is what I am doing...
NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:#"M1", #"IDMissiles",
#"G2", #"IDGuns",
#"B3", #"IDBombs",
#"KM", #"IDDistance", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:settings];
If I do the following, the values print out correctly from NSUserDefaults ...
NSLog(#"IDMissiles: %#", [userDefaults stringForKey:#"IDMissiles"]);
NSLog(#"IDGuns : %#", [userDefaults stringForKey:#"IDGuns"]);
NSLog(#"IDBombs : %#", [userDefaults stringForKey:#"IDBombs"]);
NSLog(#"IDDistance: %#", [userDefaults stringForKey:#"IDDistance"]);
However ... Each time I run the application the values in NSUserDefaults start off as (null), I was thinking that doing [[NSUserDefaults standardUserDefaults] synchronize]; would store the values for the next time I run the application, but no such luck.
Instead of using
[[NSUserDefaults standardUserDefaults] registerDefaults:settings];
try this:
[[NSUserDefaults standardUserDefaults] setObject:settings forKey:#"settings"];
Then, get from defaults like this:
NSLog(#"IDMissiles:%#[[[NSUserDefaultsstandardUserDefaults]objectForKey:#"settings"]objectForKey:#"IDMissiles"]);
One thing I discovered when working with the settings.bundle is that none of the values get initialized until you actually open the settings pane. You can have default values saved there, but they will return nil until you open the settings.
I'm not sure if this happens when you try and save values there but never open the settings pane.
If you are not using a settings pane, then you wouldn't want to use the registerDefaults option.
Try this instead.
[[NSUserDefaults standardDefaults] setObject:#"M1" forKey:#"IDMissiles"];
// set remaining values
[[NSUserDefaults standardDefaults] synchronize]; // this really only needs to be called if you plan on accessing values right away, otherwise they are saved automatically after the next run loop
From the documentation:
The contents of the registration domain are not written to disk; you need to call this method each time your application starts. You can place a plist file in the application's Resources directory and call registerDefaults: with the contents that you read in from that file.
In other words, you aren't storing anything by registering defaults like this. To have default values both in your app and in the settings bundle, you have to maintain the settings bundle separately as discussed here.
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[NSUserDefaults removeObjectForKey:#"userDefaults"];
[userDefaults setObject:[settings objectForKey:#"mainData"] forKey:#"userDefaultsValue"];
[userDefaults synchronize];

How to call a specific user default in iOS

I'm trying to call up a specific setting that is saved in the settings bundle of my iphone app. I want to do an if statement based on what was saved. My code in the implementation file looks like this:
branchMatch = [[NSString alloc] initWithString:[defaults objectForKey:#"branch"]];
The object in the settings file is just the name of the specific branch. I keep getting the SIGABRT error but I'm not sure why.
First, there's no need to initialize a string with another string. Assuming that branchMatch is a NSString*, the following would suffice:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
branchMatch = (NSString*)[defaults objectForKey:#"branch"];
Second, check if objectForKey returns a nil. Your SIGABRT is, most likely, due to a nil there.
EDIT: those values aren't present in the collection until the user opens up Settings and explicitly changes them. It's up to you to provide the sensible value if the setting is not found (is nil). The ones in the settings bundle are not automatically applied; they are only for the Settings app to initialize its UI properly.
Are you checking that the objectForKey call isn't returning nil?
Do this instead :
NSUserDefault* defaults = [NSUserDefauls standardUserDefaults];
NSString* setting = [defaults objectForKey:#"branch"];
if (setting)
// Do what needed if branch is set
else
// Do what needed if branch has never been set
After much hair pulling, I realized that I need to load user defaults from the delegate class instead of the ViewController.
Thanks for all the input

NSUserDefaults not saving properly

I have issues with NSUserDefaults and I don't quite understand what is going on.
My App has 5 levels and each level does the exact same thing with NSUserDefaults (Retrieves the levels defaults, changes the value as the user plays the level and then sets the defaults and syncronizes at the end of the level) the first 4 levels. Work without a hitch but the last level doesn't save the values. The app doesn't crash and the last level isn't the very last thing that happens, And I even have the defaults synchronized when the application terminates. Is there a max size on the NSUserDefaults or is there anything anyone can think of that I haven't, I'll post the code below but like I said the first four levels work perfectly
//header
NSUserDefaults *userData;
#property(nonatomic,retain) NSUserDefaults *userData;
//class file
//Sets the boolean variables for the class to use
userData = [NSUserDefaults standardUserDefaults];
boolOne = [userData boolForKey:#"LevelFiveBoolOne"];
boolTwo = [userData boolForKey:#"LevelFiveBoolTwo"];
boolThree = [userData boolForKey:#"LevelFiveBoolThree"];
boolFour = [userData boolForKey:#"LevelFiveBoolFour"];
boolFive = [userData boolForKey:#"LevelFiveBoolFive"];
boolSix = [userData boolForKey:#"LevelFiveBoolSix"];
boolSeven = [userData boolForKey:#"LevelFiveBoolSeven"];
//End Of Level
[userData setBool:boolOne forKey:#"LevelFiveBoolOne"];
[userData setBool:boolTwo forKey:#"LevelFiveBoolTwo"];
[userData setBool:boolThree forKey:#"LevelFiveBoolThree"];
[userData setBool:boolFour forKey:#"LevelFiveBoolFour"];
[userData setBool:boolFive forKey:#"LevelFiveBoolFive"];
[userData setBool:boolSix forKey:#"LevelFiveBoolSix"];
[userData setBool:boolSeven forKey:#"LevelFiveBoolSeven"];
[userData synchronize];
When when I switch to the view that uses these defaults they values are correct but when I terminate the application and restart it, these values aren't saved, every other level does the exact same process this is the only level that doesn't work.
I've stared at this for quite awhile and I'm hoping someone out there has run into the same problem and can give me some insight on how they resolved it.
NSUserDefaults might not have a chance to save depending on how the process is terminated.
This answer has more info: Why is NSUserDefaults not saving my values?
Just in case someone runs accross this: When storing an NSDictionary or NSArray (or mutable Objects of both of them) in the user defaults and they have an NSURL Object stored, it won't save the data on synchonize!
Somewhere you have something like:
// load the default values for the user defaults
userDefaultsValuesPath=[[NSBundle mainBundle] pathForResource:#"UserDefaults" ofType:#"plist"];
userDefaultsValuesDict=[NSDictionary dictionaryWithContentsOfFile:userDefaultsValuesPath];
// set them in the standard user defaults
[[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValuesDict];
If the initial defaults you are setting up do not have LevelFive defaults, then the calls would fail.
Check the return value from -synchronize for errors.
I decided to put this issue aside and continue development which included adding things after level five so the user can loop through levels and return to the main menu and so on and so forth...and I'm not sure why but the userDefaults are saving for level five now so I don't know if it's because before level five was the very last thing the application did and even though it didn't terminate itself and did other things maybe it wasn't actually writing the defaults to disk...I'm still not sure what was wrong but it's working now and I can't get it to fail to see if I can get an error with the synchronize...
Thanks for the help