Cannot read values from [NSUserDefaults standardUserDefaults] after synchronize - iphone

In the Application Delegate didFinishLaunching method, I am using the following code to build up a new NSDictionary to be used as the new settings bundle for the user:
NSNumber *testValue = (NSNumber*)[[NSUserDefaults standardUserDefaults] objectForKey:#"settingsversion"];
if (testValue == nil)
{
NSNumber *numNewDB = [NSNumber numberWithBool:NO];
NSNumber *numFirstUse = [NSNumber numberWithBool:YES];
NSDate *dateLastStatic = [NSDate date];
NSDate *dateLastMobile = [NSDate date];
NSNumber *numSettingsversion = [NSNumber numberWithFloat:1.0];
NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
numNewDB, #"newdb",
numFirstUse, #"firstuse",
numSettingsversion, #"settingsversion",
dateLastStatic, #"laststaticupdate",
dateLastMobile, #"lastmobileupdate",
nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults];
[[NSUserDefaults standardUserDefaults] synchronize];
}
Later in another ViewController I am trying to read back a value from that same Dictionary, saved as the NSUserDefaults - well at least I thought it would, but I don't get any valid object pointer for the desired member lastUpdate back there:
in .h file:
NSDate *lastUpdate;
in the .m file in a member function:
lastUpdate = (NSDate *)[[NSUserDefaults standardUserDefaults] objectForKey:#"laststaticupdate"];
Even, if I print out the content of [NSUserDefaults standardUserDefaults] I only get this:
2010-04-29 15:13:22.322 myApp[4136:207] Content of UserDefaults: <NSUserDefaults: 0x11d340>
This leads me to the conclusion that there is no standardUserDefaults dictionary somewhere in memory or it cannot be determined as such a structure.
Edit: Every time, I restart the app ión the device, the check for testValue is Nil and I am building up the dictionary again but after one run it should be persistent on the Phone, right?
Am I doing something wrong somewhere in between? I have the feeling that I yet didn't really understand how to load and save settings persistent for a certain application on the iPhone.
Is there anything I have to do additionally to this? Integrating a settings.bundle in XCode or saving the dictionary manually to the Documents folder?
Can someone help me out here? Thanks a lot!

registerDefaults doesn't actually write anything to disk. It just creates a 'defaults defaults' dictionary in memory. So everytime you restart the app, testValue should be nil.
If you want to set persistent values, use setObject:forKey:.
I don't know why you aren't getting a valid object back however.

Related

Why can I return a NSMutableArray from NSUserDefaults but not an NSMutableDictionary

I'm saving several items in NSUserDefaults in iOS 6. In the past I have always used an NSMutableArray that contains NSMutableDictionaries to save some info. But now I've decided it would be more efficient to use an NSMutableDictionary that contains NSMutableDictionaries. I want to be able to delete dictionaries from the main NSMutableDictionary and modify the values stored in the subdictionaries.
I've been storing my main NSMutableArray as:
-(NSMutableArray *)listArray
{
return [[NSUserDefaults standardUserDefaults] mutableArrayValueForKey:#"list_array"];
}
-(void) setDeviceListArray:(NSMutableArray *)listArray
{
[[NSUserDefaults standardUserDefaults] setObject:listArray forKey:#"list_array"];
}
I would do the same for NSMutableDictionary but there is no getter function like 'mutableDictionaryValueForKey' that exists. Can someone tell me why this is??
just because :D
copy the dictionary like this
NSMutableDictionary *d = [[NSUserDefaults standardUserDefaults] dictionaryForKey:#"bla"].mutableCopy;

How to check if NSUserDefaults exist

I have been looking for an answer but not really found what i am looking for.
I have an application and is using NSUserDefaults to store 'currentGameStatus' and would like to ask the following questions:
How do i check if the NSUserDefaults .plist exists? Need this to determine if i need to create it for the first time and if so fill it with default values
Where do i find it on my Mac (running simulator)? Would need to delete it to test if the first run works?
you don't check.
you register your defaults. and if you haven't saved a value the default will be used.
NSDictionary *defaultUserDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], #"Foo",
#"Bar", #"Baz",
[NSNumber numberWithInteger:12], #"FooBar",
nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:defaultUserDefaults];
and you do this every time your app launches.
The way I do it is I set a BOOL flag in NSUserDefaults if it doesn't already exist:
if(![[NSUserDefaults standardUserDefaults] boolForKey:#"firstRun"]) {
//do initialization stuff here...
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"firstRun"];
}
NSUserDefaults already exists by default. You can add to it by [[NSUserDefaults standardUserDefaults] setObject:#"object" forKey:#"key"];
You can find the NSUserDefaults .plist here

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];
}

how can i store value in an NSArray using WritetoFile?

i wana store the index of seleted cell of table using NSArray, can u help me....
You can use user defaults or property list for this.
Example on user defaults. You have a controller class that has access to the index and will load it at startup and write it into plist whenever it's updated:
If you have some kind of controller class then you would put this code into + (void)initialize, it initialises the variable if it does not exists in plist:
+ (void)initialize
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *appDefaults =
[NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:5]
forKey:#"MyFunnyIndex"];
[defaults registerDefaults:appDefaults];
}
In your -(void)awakeFromNib (I'm assuming you're using some kind of controller class) load your last stored value:
-(void)awakeFromNib
{
int index =
[[NSUserDefaults standardUserDefaults] integerForKey:#"MyFunnyIndex"];
[somethingThatNeedsIndex setIndex:index];
// ...
}
Somewhere where the index is updated (or where you want to write it to plist), let's call it - (void)updateInterface:
- (void)updateInterface
{
[[NSUserDefaults standardUserDefaults]
setObject:[NSNumber numberWithInteger:index]
forKey:#"MyFunnyIndex"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
I don't know if I understand the question correctly, but it sounds like you could use a property list to store this information. Property lists are very easy to use and quite efficient with small amounts of data.
Read the "Property List Programming Guide" for further explanation. There is even a tutorial in there.