Saving a single NSMutableDictionary in a plist - iphone

I have one dictionary I need to save into a plist. The paletteDictionary always returns nil:
- (void)saveUserPalette:(id) sender
{
[paletteDictionary setObject:matchedPaletteColor1Array forKey:#"1"];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"UserPaletteData.plist"];
// write plist to disk
[paletteDictionary writeToFile:path atomically:YES];
}
I'm reading the data back in a different view like:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"UserPaletteData.plist"];
NSMutableDictionary *plistDictionary = [NSMutableDictionary dictionaryWithContentsOfFile:path];
if(plistDictionary==nil ){
NSLog(#"failed to retrieve dictionary from disk");
}

Can you mention what object is "matchedPaletteColor1Array". I believe, its some custom class object. With my knowledge the writable objects must conform to NSCoding, which NSString,NSDate etc are already, So in given code, setting "matchedPaletteColor1Array" as some string will work.
You can look at following post to make your object NSCoding enabled. objects conforming to nscoding will not writetofile

Run your application in the simulator and browse to the following path:
~/Library/Application Support/iPhone Simulator/User/Applications/{A GUID}/Documents
Where A GUID will be a string like this: 06430A38-AFAC-4C68-8F39-DBD6C81A5AA6 (it's probably the Last Modified folder).
Verify that UserPaletteData.plist is present and load it up in Property List Editor.app to verify that it contains some data.
Also make sure that the you use only the following data types in your dictionary otherwise it will fail to write to a plist: (NSData, NSDate, NSNumber, NSString, NSArray, or NSDictionary).
To verify that the dictionary you are attempting to save to disk is valid for a plist, try the following:
for (id key in paletteDictionary)
{
NSLog(#"key: %#, value: %#, class: %#", key, [paletteDictionary objectForKey:key], NSStringFromClass([[paletteDictionary objectForKey:key] class]));
}
That should tell you if any of your objects are the wrong data type for a plist.
If that is still not helping, then you should make sure that paletteDictionary has been alloc/init'ed before saveUserPalette is called.

Check that you are allocating (alloc/init) paletteDictionary before you use it.

Related

Write JSON Response to .plist File

Frustration on the Top !!!
I am getting some JSON Response from the Service and I want to store it in the .plist file for Future Reference.
I am unable to save my JSON Response to .plist File. I think it's due to some null values into the Response.
Note : I confirmed that the Response is in JSON Format using jsonparser.
My Code :
NSError *error;
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
NSDictionary *dictResult = [(NSDictionary*)json objectForKey:#"myKey"];
NSLog(#"Result Dictionary :: %#",dictResult);
NSURL *cacheDir = [[[NSFileManager defaultManager] URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *path = [cacheDir URLByAppendingPathComponent:#"FinalResult.plist"];
NSLog(#"Path :: %#",path);
BOOL success = [dictResult writeToURL:path atomically:YES];
NSLog(#"success? %d", success);
Note : I got all the NSLog Values (means the Response Dictionary and File Path but 0 for success).
Problem : There are almost 70-80 key-value pairs in the Response and I don't want to remove/replace all the null values. Because What I want is ...
GET the Response From the Server.
Fill all the UITextFields with the Response.
POST the Same Response to the Server with some Edited Values from the UITextFields.
So, I just want to change the Edited UITextField values in the Object and let it POST to the Server.
What is the Optimum Way to Fix this ?
I bet that your JSON contains at least one null value.
When you have JSON that contains null and you convert it using NSJSONSerialization, the null is replaced by an instance of NSNull. If your dictionary contains NSNull, then writeToURL:atomically: will fail.
This is because the convenience methods for reading and writing dictionaries (and arrays) only work if the data in the collection is restricted to property list types. These are:
NSString
NSNumber
NSData
NSDate
NSArray
NSDictionary. And for dictionaries, the keys must be NSStrings.
You can also use mutable subclasses (like NSMutableString) when writing.
If you have anything not on that list, you can't use writeToURL:atomically or any of the similar convenience methods.
The problem is that some valid JSON can't be converted to property lists. Also, some valid property lists can't be converted to JSON (because NSDate won't automatically convert to valid JSON).
If it was me, I'd just write the data to a JSON file. Leave it in its original format. You can convert to/from JSON easily, so leave it that way.
If your dictionary contains NSNull, then writeToURL:atomically: will fail.
For dictionaries, the keys must be NSStrings.
The problem is that some valid JSON can't be converted to property lists. Also, some valid property lists can't be converted to JSON.
Don't Forget, If you must use a property list, you will need to scan the entire dictionary and convert the nulls into something that can be saved in a property list file.
Only Solution is that you have to check all the NULL Values and Replace it with #" ".
Happy Coding...
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *libraryPath = [paths objectAtIndex:0];
NSString *filename = #"FinalResult.plist";
NSString *pathFilename = [libraryPath stringByAppendingPathComponent:filename];
NSDictionary *dictResult = [json objectForKey:#"myKey"];
[dictResult writeToFile:pathFilename atomically:YES];
I build my file urls this way:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *path = [paths objectAtIndex:0];
NSString *filename = #"FinalResult.plist";
NSString *pathFilename = [path stringByAppendingPathComponent:filename];
Then see if this writes:
BOOL success = [dictResult writeToFile:pathFilename atomically:YES];
NSLog(#"success? %d", success);
Edit - Funny, I just recently confronted this problem and then forgot all about it. Some JSON parsers will use [NSNull null] as placeholders for nil values. I wrote this (seriously, just about two weeks ago and then spaced on it) to clean up the parse result...
- (NSDictionary *)compact:(NSDictionary *)aDictionary {
NSDictionary *answer = [NSMutableDictionary dictionary];
for (NSString *key in [aDictionary allKeys]) {
id value = [self.dictionary valueForKey:key];
if (value && value != [NSNull null]) {
[answer setValue:value forKey:key];
}
}
return [NSDictionary dictionaryWithDictionary:answer];
}
This could be made into a NSDictionary category addition if you wanted.
Check if the return value of the writeToURL:atomically: method returns YES.
If it doesn't, check if you have write permissions to that URL.
Try it with another URL to find out if its the path or the contents of the dictionary that's causing this error.
Try this, it will work :
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"country.plist"];
[jsonData writeToFile:path atomically:YES];

Xcode 4 creating .plist

I have interesting thing here, I'm using xcode 4 for my project and I want to create simple .plist file to save several values which will be used for different purposes during code execution. This is the code I use to create plist:
- (NSString *)dataFilePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:#"somelist.plist];
}
last line of code which should append plist name to path do not do its job. Plist is never created. Now, strange thing, i manage to create one few days ago using same code. After that, I only move this piece of code in AppDelegate because, I call this code from different places. I discovered that isn't working when I switch to 4.3 emulator and then also try it on iphone device. When I switch to 4.2 emulator it works because there is already a plist, however when I change its name (to create new one) nothing happens - meaning, list is not created. I also try moving code back to original place, but that didn't gave desired result.
Your code should not create a plist anyway, only return a path. In order to actually save a plist at the chosen path, you could choose several approaches, for example use the following method on NSDictionary:
- (void)saveDictionary
{
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:#"bla" forKey:#"test"];
NSString *path = [self datafilePath];
[dictionary writeToFile:path atomically:YES];
}
- (NSString *)dataFilePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:#"somelist.plist];
}

Problem saving a plist

I was trying to use a plist to store an array with the below code:
NSString *name = firstName.text;
NSString *path = [[NSBundle mainBundle] pathForResource:#"Names" ofType:#"plist"];
NSMutableArray *namesArray = [[NSMutableArray alloc] initWithContentsOfFile:path];
[namesArray addObject:name];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
[paths release];
NSString *docDirPath = [documentsDirectory stringByAppendingPathComponent:#"Names.plist"];
[namesArray writeToFile:docDirPath atomically:YES];
namesArray = [[NSMutableArray alloc] initWithContentsOfFile:docDirPath];
This code seems to work. Using NSLog, I have found that after this code executes the plist contains what I want it to, however, my program crashes because it generates an EXC_BAD_ACCESS on a device, and on the simulator it just crashes without an explanation. Does anyone know why that might happen?
NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES); //Auto-released array
NSString *documentsDirectory = [paths objectAtIndex:0];
[paths release]; //Oh noes!
You don't own the reference to paths, so don't release it. Remove [paths release] and I'll bet you're fine. You're crashing because the autorelease pool is releasing paths after you've already done it yourself.
Quoth the guide:
You only release or autorelease objects you own. You take ownership of an object if you create it using a method whose name begins with “alloc” or “new” or contains “copy” ... or if you send it a retain message.
Have you checked, at which place it is giving EXC_BAD_ACCESS error.
In your code there are two wrong things; those are.
The Plist file consists a dictionary not an array, Here in the code you are copying the file data to an array. and saving the array to the plist file.
Second one is you are releasing the "paths" array, with out completion of usage of it. you have to release that array at the end of the statements; like after updating the array to the file.
Regards,
Satya

Save NSMutableArray to load when reopenning iphone app

I have a NSMutableArray, each item in this array is different class. In each class has many field such as CPPlot, identifier,... (I am using CorePlot to develop a stock application). Now I would like to store this NSMutableArray to load when user reopen application, this will load all the chart they used before.
I try to figure out how to do that in Stackoverflow. And I found out there were 2 solutions:
NSUserDefaults
SQLite database
In NSUserDefaults, when I want to store NSMutableArray, I must implement with NSKeyedArchiver to archive and unarchive array object, also do NSCoding protocol for each item in array object. But I can not do this solution because in each item, it has some fields from CorePlot library, so that I can not use NSCoding to these fields.
SQLite database, I can not use this solution because each item in array object is different class.
I would like to ask if any other solution to solve this problem?
I hope my words are clear enough to understand.
Thanks
I would suggest you figure out what kind of data is at the root of your CorePlot objects. If it is integers, then simply store them in NSUserDefaults, and then simply rebuild your NSMutableArray on re-opening the app. Another option is to store your items in a separate plist file.
Use this method to save:
- (NSArray *)applicationDataFromFile:(NSString *)fileName {
NSArray *paths = NSSearchPathForDirecotiresInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *appFile = [documentsDirectory
stringByAppendingPathComponent:fileName];
NSArray *myData = [NSArray arrayWithContentsOfFile:appFile];
return myData;
}
- (BOOL)saveToFileForStringArray:(NSMutableArray *)array
toFile:(NSString *)fileName {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
if (!documentsDirectory) {
NSLog(#"Documents directory not found!");
return NO;
}
NSString *appFile = [documentsDirectory
stringByAppendingPathComponent:fileName];
return ([array writeToFile:appFile atomically:YES]);
}

IPHONE: Saving and Retrieving an Dictionary of Dictionaries from a plist

I have a main dictionary where each entry is a dictionary. I need to save this to a plist and then later retrieve its contents.
This is what I am doing to save the dictionary
// create a dictionary to store a fruit's characteristics
NSMutableDictionary *fruit = [[NSMutableDictionary alloc] init];
[fruit setObject:quantity forKey:#"quantity"];
[fruit setObject:productID forKey:#"productID"];
[fruit setObject:nameID forKey:#"nameID"];
// create a dictionary to store all fruits
NSMutableDictionary *stock = [[NSMutableDictionary alloc] init];
[stock setObject:fruit forKey:#"nameID"];
... after adding all fruits to the stock dictionary, write the stock to a plist
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"stock.plist"];
NSMutableDictionary *stock = [NSMutableDictionary dictionaryWithContentsOfFile:path];
[stock writeToFile:path atomically:YES];
... to restore the dictionary, I use
NSMutableDictionary *stock = [NSMutableDictionary dictionaryWithContentsOfFile:path];
... but this is not saving anything to the file... what am I missing?
thanks for any help.
You write:
... after adding all fruits to the
stock dictionary, write the stock to a
plist
but your code is reading from disk before you write the stock dictionary to disk. So with the assumption that stock.plist doesn't actually exist at that path, you've just set stock to nil, so after that you're sending the writeToFilePath message to nil.
try this:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"stock.plist"];
// write plist to disk
[stock writeToFile:path atomically:YES];
// read it back in with different dictionary variable
NSMutableDictionary *savedStock = [NSMutableDictionary dictionaryWithContentsOfFile:path];
if( savedStock==nil ){
NSLog(#"failed to retrieve dictionary from disk");
}
Finally, what data types are quantity and productID? you cannot serialize non-object data types, so if quantity is an integer, you would need to wrap it like so:
[fruit setObject:[NSNumber numberWithInt:quantity] forKey:#"quantity"];
Spend some time reading about property list serialization.
dictionaryWithContentsOfFile doesn't save, it reads a file. I don't see any code which writes to the file.
You are going to need something like this in your save code:
[stock writeToFile:path atomically:YES];
You're (re-)creating stock with the contents of the file immediately before writing it. Since the file doesn't exist, the dictionary is now nil. When you attempt to write that out, it doesn't produce anything. Instead, you should use the version of stock that you already populated.
(Assuming the saving bit is in the same scope, just delete the line starting NSMutableDictionary *stock above the call to writeToFile.)
(Although, come to think of it, it can't be in the same scope or the compiler would have complained in the first place.)