Why does NSUserDefaults fail to save NSMutableDictionary? - iphone

I’m trying to save a NSMutableDictionary with NSUserDefaults. I read many posts on the topic in stackoverflow... I also found one option that worked; however unfortunately it worked only once and then it started to save (null) only.
Does anybody have a hint?
Thanks
Code to save:
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:dictionary] forKey:#"Key"];
[[NSUserDefaults standardUserDefaults] synchronize];
Code to load:
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc]init];
NSData *data = [[NSUserDefaults standardUserDefaults]objectForKey:#"Key"];
dictionary = [NSKeyedUnarchiver unarchiveObjectWithData:data];
Code to add Objects to the NSMutableDictionary:
[dictionary setObject:[NSNumber numberWithInt:0] forKey:#"Key 1"];
[dictionary setObject:[NSNumber numberWithInt:1] forKey:#"Key 2"];
[dictionary setObject:[NSNumber numberWithInt:2] forKey:#"Key 3"];
Code to NSLog() values:
for (NSString * key in [dictionary allKeys]) {
NSLog(#"key: %#, value: %i", key, [[dictionary objectForKey:key]integerValue]);
}
And also the keys are (null):
NSLog(#"%#"[dictionary allKeys]);

From Apple's documentation for NSUserDefaults objectForKey:
The returned object is immutable, even if the value you originally set was mutable.
The line:
dictionary = [NSKeyedUnarchiver unarchiveObjectWithData:data];
discards the previously created NSMutableDictionary and returns a NSDictionary.
Change the loading to:
NSData *data = [[NSUserDefaults standardUserDefaults]objectForKey:#"Key"];
dictionary = [NSKeyedUnarchiver unarchiveObjectWithData:data];
Complete example, there is also no need to use NSKeyedArchiver in this example:
NSDictionary *firstDictionary = #{#"Key 4":#4};
[[NSUserDefaults standardUserDefaults] setObject:firstDictionary forKey:#"Key"];
NSMutableDictionary *dictionary = [[[NSUserDefaults standardUserDefaults] objectForKey:#"Key"] mutableCopy];
dictionary[#"Key 1"] = #0;
dictionary[#"Key 2"] = #1;
dictionary[#"Key 3"] = #2;
for (NSString * key in [dictionary allKeys]) {
NSLog(#"key: %#, value: %#", key, [dictionary objectForKey:key]);
}
NSLog output:
key: Key 2, value: 1
key: Key 1, value: 0
key: Key 4, value: 4
key: Key 3, value: 2

Related

Pre-Populating Core Data with plist on applications first launch only

I have a plist file which is an array of dictionaries. Where each dictionary contains a set of strings. Each dictionary represents a celebrity.
What I would like to do is populate Core Data with the contents of this plist on the applications first launch, after which I would like to somehow check core data for the existence of my data, and if there is data, load it from there, otherwise load the initial data from the plist file again.
I know its possible to populate core data from a plist, but is what I'm suggesting a viable solution? Or is there a better approach?
Jack
My sample code
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if (![defaults objectForKey:#"dataImported"]) {
NSString *path = [[NSBundle mainBundle] pathForResource:#"dict" ofType:#"plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
for (NSString *key in [dict allKeys]) {
NSDictionary *node = [dict objectForKey:key];
MyClass *newObj = .....
}
[defaults setObject:#"OK" forKey:#"dataImported"];
[defaults synchronize];
}
This is doing the same thing but with a slightly more complex pList that contains an array of dictionaries representing data on a "topic" to be stored. It still has some of the debug logging in place. Hope it is useful to someone.
NSManagedObjectContext *context = self.managedObjectContext;
NSError *error;
NSFetchRequest *topicRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *topicEntityDescription = [NSEntityDescription entityForName:#"Topic" inManagedObjectContext:context];
[topicRequest setEntity:topicEntityDescription];
NSManagedObject *newTopic = nil;
NSArray *topics = [context executeFetchRequest:topicRequest error:&error];
if (error) NSLog(#"Error encountered in executing topic fetch request: %#", error);
if ([topics count] == 0) // No topics in database so we proceed to populate the database
{
NSString *topicsPath = [[NSBundle mainBundle] pathForResource:#"topicsData" ofType:#"plist"];
NSArray *topicsDataArray = [[NSArray alloc] initWithContentsOfFile:topicsPath];
int numberOfTopics = [topicsDataArray count];
for (int i = 0; i<numberOfTopics; i++)
{
NSDictionary *topicDataDictionary = [topicsDataArray objectAtIndex:i];
newTopic = [NSEntityDescription insertNewObjectForEntityForName:#"Topic" inManagedObjectContext:context];
[newTopic setValuesForKeysWithDictionary:topicDataDictionary];
[context save:&error];
if (error) NSLog(#"Error encountered in saving topic entity, %d, %#, Hint: check that the structure of the pList matches Core Data: %#",i, newTopic, error);
};
}

NSUserDefault to NSArray

Icstored the value of 2 label in NSUserDefault, trought NSMutableArray.
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
// Add a bookmark
NSMutableDictionary *bookmark = [NSMutableDictionary new];
[bookmark setValue:author.text forKey:#"author"];
[bookmark setValue:book.text forKey:#"book"];
[bookmarks addObject:bookmark];
// Save your (updated) bookmarks
[userDefaults setObject:bookmarks forKey:#"bookmarks"];
[userDefaults synchronize];
Now my problem is how retrieve the values of NSUserDefault trought NSArray?
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
self.dataArray = [prefs arrayForKey:#"bookmarks"];
NSString *author = ??
NSString *book = ??
NSString * author = [self.dataArray objectForKey: #"author"];
NSString * book = [self.dataArray objectForKey: #"book"];
This assumes you successfully saved the array and that you successfully re-loaded the array from NSUserDefaults. You really should do some error checking in your code.
In the dataArray, you can store lots of bookmarks.This methods can log all of the bookmarks.
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
self.dataArray = [prefs objectForKey:#"bookmarks"];
for(int i=0;i<[dataArray count];i++){
NSDictionary *dic = [dataArray objectAtIndex:i];
NSLog(#"%#",dic);
NSString * author = [dic objectForKey: #"author"];
NSString * book = [dic objectForKey: #"book"];
}
you have inserted an array with a dictionary. you can retrieve it by using
self.dataArray = [prefs arrayForKey:#"bookmarks"];
NSString *author = [self.dataArray objectAtIndex:0] objectForKey:#"author"];
NSString *book = [self.dataArray objectAtIndex:0] objectForKey:#"book"];
But it is better to store the dictionary directly in user defaults.

Complex Json Data and NSMutableDictionary parsing issue

Hi I'm using SBJson for moving Json data in and out of NSMutableDictionar, Im Building the main NSMutableDictionary from few others NSMutableDictionary like this
- (NSMutableDictionary *) getGeneral{
NSMutableDictionary *pType = [[NSMutableDictionary alloc]init];
[pType setObject:[NSNumber numberWithInteger:3] forKey:#"Ptype"];
NSMutableDictionary *session = [[NSMutableDictionary alloc]init];
[session setObject:[NSNumber numberWithInteger:-1] forKey:#"user_id"];
[session setObject:#"3" forKey:#"device_token"];
[session setObject:[NSNumber numberWithInteger:-1] forKey:#"customer_id"];
[session setObject:#"3" forKey:#"client_time"];
NSMutableDictionary *Error = [[NSMutableDictionary alloc]init];
[Error setObject:[NSNumber numberWithInteger:-1] forKey:#"error_code"];
[Error setObject:#"3" forKey:#"error_message"];
NSMutableDictionary *Successful = [[NSMutableDictionary alloc]init];
[Successful setObject:[NSNumber numberWithInteger:-1] forKey:#"success_code"];
[Successful setObject:#"3" forKey:#"success_message"];
NSMutableDictionary *Details = [[NSMutableDictionary alloc]init];
[Details setObject:#"3" forKey:#"user_name" ];
[Details setObject:#"3" forKey:#"user_password" ];
[Details setObject:[NSNumber numberWithInteger:-1] forKey:#"StartCallID"];
[Details setObject:#"3" forKey:#"StartDate" ];
[Details setObject:#"3" forKey:#"EndDate"];
NSMutableDictionary *General = [[NSMutableDictionary alloc]init];
[General setObject:pType forKey:#"Ptype"];
[General setObject:session forKey:#"Session"];
[General setObject:Error forKey:#"Error"];
[General setObject:Successful forKey:#"Successful"];
[General setObject:Details forKey:#"Details"];
return General;
}
and then i assign data into it i expected to get this Json structure:
{
"Ptype":[{"Ptype":-1}],
"Session":[{
"user_id":-1,
"device_token":" ",
"customer_id":-1,
"client_time":"",
}],
"Error":[{"error_code":-1,
"error_message":""}],
"Successful":[{"success_code":-1,
"success_message":""}],
"Details":[{
"user_name":" ",
"user_password":" ",
"StartCallID":-1,
"StartDate":" ",
"EndDate":" "
}]}
but there is no "]" or "[" in my json it looks like this, the order also change but this is not a problem, i take care of it on the server, issue is no square brackets
{"Session":
{"customer_id":-1,
"client_time":"3",
"user_id":-1,
"device_token":"3"},
"Error":{"error_code":-1,"error_message":"3"},
"Successful":{"success_code":-1,"success_message":"3"},
"Details":{"StartCallID":-1,
"user_password":"gg",
"user_name":"ff",
"StartDate":"3",
"EndDate":"3"},
"Ptype":{"Ptype":3}}
any one know on this issue, i need multiple items with the same name and this is the json standart for it
Thanks
Square brackets surrounds an array and you only have dictionaries.
The key is unique within each dictionary.
For example to put customer_id in a dictionary within an array:
NSArray *myArray = [NSArray arrayWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:
#"-1",
#"customer_id",
nil] nil];
Link of interest Understand JSON 3min lesson

How to access values from settings.bundle?

I am doing a project where I have to retrieve some values in settings.bundle. For this I have to retrieve a default value in such a manner.
I am accessing it as under
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
stringObject = [defaults objectForKey:#"Key_Value"];
But when I try to print stringObject
NSLog(#"%#",stringObject);
Then it always print null.
I have saved a string value in the settings.bundle with key "Key_Value". But It returns Null.
tell me where I am doing wrong
check you have stored you value in this way:-
NSUserDefaults *pref3=[NSUserDefaults standardUserDefaults];
[pref3 setObject:*yourstring* forKey:#"Key_Value"];
[NSUserDefaults resetStandardUserDefaults];
your code of retreiving is correct.make sure stringObject is of NSString type.
Call this function will resolve your issue
- (void)registerDefaultsFromSettingsBundle {
// this function writes default settings as settings
NSString *settingsBundle = [[NSBundle mainBundle] pathForResource:#"Settings" ofType:#"bundle"];
if(!settingsBundle) {
NSLog(#"Could not find Settings.bundle");
return;
}
NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[settingsBundle stringByAppendingPathComponent:#"Root.plist"]];
NSArray *preferences = [settings objectForKey:#"PreferenceSpecifiers"];
NSMutableDictionary *defaultsToRegister = [[NSMutableDictionary alloc] initWithCapacity:[preferences count]];
for(NSDictionary *prefSpecification in preferences) {
NSString *key = [prefSpecification objectForKey:#"Key"];
if(key) {
[defaultsToRegister setObject:[prefSpecification objectForKey:#"DefaultValue"] forKey:key];
NSLog(#"writing as default %# to the key %#",[prefSpecification objectForKey:#"DefaultValue"],key);
}
[[NSUserDefaults standardUserDefaults] registerDefaults:defaultsToRegister];
}

How can we store into an NSDictionary? What is the difference between NSDictionary and NSMutableDictionary?

I am developing an application in which I want to use an NSDictionary. Can anyone please send me a sample code explaining the procedure how to use an NSDictionary to store Data with a perfect example?
The NSDictionary and NSMutableDictionary docs are probably your best bet. They even have some great examples on how to do various things, like...
...create an NSDictionary
NSArray *keys = [NSArray arrayWithObjects:#"key1", #"key2", nil];
NSArray *objects = [NSArray arrayWithObjects:#"value1", #"value2", nil];
NSDictionary *dictionary = [NSDictionary dictionaryWithObjects:objects
forKeys:keys];
...iterate over it
for (id key in dictionary) {
NSLog(#"key: %#, value: %#", key, [dictionary objectForKey:key]);
}
...make it mutable
NSMutableDictionary *mutableDict = [dictionary mutableCopy];
Note: historic version before 2010: [[dictionary mutableCopy] autorelease]
...and alter it
[mutableDict setObject:#"value3" forKey:#"key3"];
...then store it to a file
[mutableDict writeToFile:#"path/to/file" atomically:YES];
...and read it back again
NSMutableDictionary *anotherDict = [NSMutableDictionary dictionaryWithContentsOfFile:#"path/to/file"];
...read a value
NSString *x = [anotherDict objectForKey:#"key1"];
...check if a key exists
if ( [anotherDict objectForKey:#"key999"] == nil ) NSLog(#"that key is not there");
...use scary futuristic syntax
From 2014 you can actually just type dict[#"key"] rather than [dict objectForKey:#"key"]
NSDictionary *dict = [NSDictionary dictionaryWithObject: #"String" forKey: #"Test"];
NSMutableDictionary *anotherDict = [NSMutableDictionary dictionary];
[anotherDict setObject: dict forKey: "sub-dictionary-key"];
[anotherDict setObject: #"Another String" forKey: #"another test"];
NSLog(#"Dictionary: %#, Mutable Dictionary: %#", dict, anotherDict);
// now we can save these to a file
NSString *savePath = [#"~/Documents/Saved.data" stringByExpandingTildeInPath];
[anotherDict writeToFile: savePath atomically: YES];
//and restore them
NSMutableDictionary *restored = [NSDictionary dictionaryWithContentsOfFile: savePath];
The key difference: NSMutableDictionary can be modified in place, NSDictionary cannot. This is true for all the other NSMutable* classes in Cocoa. NSMutableDictionary is a subclass of NSDictionary, so everything you can do with NSDictionary you can do with both. However, NSMutableDictionary also adds complementary methods to modify things in place, such as the method setObject:forKey:.
You can convert between the two like this:
NSMutableDictionary *mutable = [[dict mutableCopy] autorelease];
NSDictionary *dict = [[mutable copy] autorelease];
Presumably you want to store data by writing it to a file. NSDictionary has a method to do this (which also works with NSMutableDictionary):
BOOL success = [dict writeToFile:#"/file/path" atomically:YES];
To read a dictionary from a file, there's a corresponding method:
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:#"/file/path"];
If you want to read the file as an NSMutableDictionary, simply use:
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfFile:#"/file/path"];