Writing NSArray into plist - objective-c

I'm trying to save a NSMutableArray into a pre-existing plist.
When I try:
NSError *errorDesc;
NSPropertyListFormat format;
NSString *plistPath;
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES) objectAtIndex:0];
plistPath = [rootPath stringByAppendingPathComponent:#"Array_Label_Generiche.plist"];
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath]) {
NSLog(#"prima volta");
plistPath = [[NSBundle mainBundle] pathForResource:#"Array_Label_Generiche" ofType:#"plist"];
}
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
NSMutableArray *temp = (NSMutableArray *)[NSPropertyListSerialization
propertyListFromData:plistXML
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:&format
errorDescription:&errorDesc];
if (!temp) {
NSLog(#"Error reading plist: %#, format: %lu", errorDesc, format);
}
contatore = (int)temp.count ;
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc]init];
[dictionary setObject:array_copiato forKey:[NSString stringWithFormat: #"%#", [alertView textFieldAtIndex:0].text]];
NSDictionary *dictionary_da_salvare = [[NSDictionary alloc ] initWithDictionary: dictionary];
//AGGIUNGO I DATI CREATI
NSLog(#"TEMP %#",temp);
[temp addObject:dictionary_da_salvare];
BOOL didWriteArray = [temp writeToFile:plistPath atomically:YES];
if (didWriteArray)
{
NSLog(#"Write to .plist file is a SUCCESS!");
}
else
{
NSLog(#"Write to .plist file is a FAILURE!");
}
In this case, output show me that "Write to .plist file is a FAILURE!".
In the Apple Guide, I read that I must serialize data before writing on the plist.
Then I tried to write these lines of code:
NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:temp format:NSPropertyListXMLFormat_v1_0 errorDescription:&errorDesc];
if(plistData == nil){
NSLog (#"error writing to file: %#", errorDesc);
}
When I run the application, the output area reads:
error writing to file: Property list invalid for format (property
lists cannot contain objects of type 'CFType') Write to .plist file
is a FAILURE!
I think because the array test is NSMutableArray because I have read in the Apple Guide:
The NSPropertyListSerialization class provides methods that convert
property list objects to and from several serialized formats. Property
list objects include NSData, NSString, NSArray, NSDictionary, NSDate,
and NSNumber objects. These objects are toll-free bridged with their
respective Core Foundation types (CFData, CFString, and so on).

Related

Creating a plist with multiple strings

I have a multiple strings I would like to write to one plist using objective c. Can anyone please tell me exactly how to do this? I appreciate it
As H2CO3 hinted, you could use NSArray's writeToFile:atomically: method.
For example:
NSArray *arr = #[
#"my first string",
#"my second string",
#"and the last one"
];
[arr writeToFile:#"./out.plist" atomically:NO]; // Or YES depending on your needs
Here's one possibility:
// Create the path that you want to write your plist to.
NSError *error = nil;
NSURL *documentsURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:&error];
if (documentsURL == nil) {
NSLog(#"Error finding user documents in directory: %#", [error localizedDescription]);
return nil;
}
NSString *path = [[documentsURL path] stringByAppendingPathComponent:#"YourFile.plist"];
// Populate your strings and save to the plist specified in the above path.
NSString *kRoot = #"kRoot";
NSMutableDictionary *tempDict = [NSMutableDictionary dictionary];
tempDict[kRoot] = [NSMutableArray array];
[tempDict[kRoot] addObject:#"String 1"];
[tempDict[kRoot] addObject:#"String 2"];
[tempDict[kRoot] addObject:#"String 3"];
// Etc, add all your strings
if (![tempDict writeToFile:path atomically:YES])
{
NSLog(#"Error writing data to path %#", path);
}

How to save data locally in app? [duplicate]

This question already has answers here:
Working with data in iOS Apps (What to choose? NSData, CoreData, sqlite, PList, NSUserDefaults)
(2 answers)
Closed 9 years ago.
I've been struggling with this for ages now and I really need some good help here. :)
I have an app where I'm parsing a quite big JSON into appdelegate's didFinishLaunchingWithOptions.
My Model Objects are:
Tab:
NSString *title
NSMutableArray *categories
Category:
NSString *title
NSMutableArray *items
Item
NSString *title
NSString *description
UIImage *image
I need to save the data locally, cause the parsing takes about 15 seconds every time my app starts. I'm using the SBJSON framework.
Here's my code for parsing:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"json_template" ofType:#"json"];
NSString *contents = [NSString stringWithContentsOfFile: filePath encoding: NSUTF8StringEncoding error: nil];
SBJsonParser *jsonParser = [[SBJsonParser alloc] init];
NSMutableDictionary *json = [jsonParser objectWithString: contents];
tabs = [[NSMutableArray alloc] init];
jsonParser = nil;
for (NSString *tab in json)
{
Tab *tabObj = [[Tab alloc] init];
tabObj.title = tab;
NSDictionary *categoryDict = [[json valueForKey: tabObj.title] objectAtIndex: 0];
for (NSString *key in categoryDict)
{
Category *catObj = [[Category alloc] init];
catObj.name = key;
NSArray *items = [categoryDict objectForKey:key];
for (NSDictionary *dict in items)
{
Item *item = [[Item alloc] init];
item.title = [dict objectForKey: #"title"];
item.desc = [dict objectForKey: #"description"];
item.url = [dict objectForKey: #"url"];
if([dict objectForKey: #"image"] != [NSNull null])
{
NSURL *imgUrl = [NSURL URLWithString: [dict objectForKey: #"image"]];
NSData *imageData = [NSData dataWithContentsOfURL: imgUrl];
item.image = [UIImage imageWithData: imageData];
}
else
{
UIImage *image = [UIImage imageNamed: #"standard.png"];
item.image = image;
}
[catObj.items addObject: item];
}
[tabObj.categories addObject: catObj];
}
[tabs addObject: tabObj];
}
What is the best way of doing this? Using Core Data or NSFileManager?
If you have som code example too it will make me very happy.
This is the last thing i need to fix before the app is ready for app store and it just kills me! I can't solve this problem.
If you are working on iOS then you save a file to the Documents folder. On Mac OS X it would be in the Application Support folder. Since you are on iOS, read this answer for how to access the Documents folder.
All of the objects that you want to store should implement NSCoding. The above variables already do. Should you want to store the tabs, categories and items directly they would need to implement NSCoding. Then all you need is to serialize them to a file. When opening you app you can look for this file and get your objects back without parsing.
The code should look something like this (untested and error checking is ommited for brevity):
- (void) saveStateToDocumentNamed:(NSString*)docName
{
NSError *error;
NSFileManager *fileMan = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = [paths[0] stringByAppendingPathComponent:docName];
if ([fileMan fileExistsAtPath:docPath])
[fileMan removeItemAtPath:docPath error:&error];
// Create the dictionary with all the stuff you want to store locally
NSDictionary *state = #{ ... };
// There are many ways to write the state to a file. This is the simplest
// but lacks error checking and recovery options.
[NSKeyedArchiver archiveRootObject:state toFile:docPath];
}
- (NSDictionary*) stateFromDocumentNamed:(NSString*)docName
{
NSError *error;
NSFileManager *fileMan = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = [paths[0] stringByAppendingPathComponent:docName];
if ([fileMan fileExistsAtPath:docPath])
return [NSKeyedUnarchiver unarchiveObjectWithFile:docPath];
return nil;
}

Objective C - fatal error

I have a problem with my Xcode app, When I push a button, my app crashes.
Here is my button's action, I also declared variables, strings, etc... but it isn't in this code:
{
NSLog(#" - Writing Data.plist Labels");
NSString *error;
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *plistPath = [rootPath stringByAppendingPathComponent:#"Data.plist"];
NSDictionary *plistDict = [NSDictionary dictionaryWithObjects:
[NSArray arrayWithObjects: compteur01, compteur02, compteur03, compteur04, compteur05, compteur06, nil]
forKeys:[NSArray arrayWithObjects: #"name1", #"name2", #"name3", #"name4", #"name5", #"name6", nil]];
NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:plistDict
format:NSPropertyListXMLFormat_v1_0
errorDescription:&error];
if(plistData) {
[plistData writeToFile:plistPath atomically:YES];
}
else {
NSLog(#"Error writeToFile:plistData:labels");
[error release];
}
}
Aparently compteur01 is nil.
Most likely the other compteur0x are nil too. But for this message at least the first one is nil.
Make sure that none of the objects added to the dictionary are nil. Use NSNULL instead if you need them to represent a nil/null value.

Can't figure out about saving files

I am trying to save my object to the file system on an iPad, but I seem to be doing something wrong. Here is how I have archived the object:
NSString *localizedPath = [self getPlistFilePath];
NSString *fileName = [NSString stringWithFormat:#"%#.plist", character.infoName];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:character];
fileName = [fileName stringByReplacingOccurrencesOfString:#" " withString:#"_"];
localizedPath = [localizedPath stringByAppendingPathComponent:fileName];
NSLog(#"File Path: %#", localizedPath);
if(data) {
NSError *writingError;
BOOL wasWritten = [data writeToFile:localizedPath options:NSDataWritingAtomic error:&writingError];
if(!wasWritten) {
NSLog(#"%#", [writingError localizedDescription]);
}
}
Now, this creates a plist file that I can see and read on the file system. When I try to use the following to unarchive it though:
NSError *error;
NSString *directory = [self getPlistFilePath];
NSArray *files = [[NSFileManager defaultManager]contentsOfDirectoryAtPath:directory error:&error];
NSMutableArray *characters = [[NSMutableArray alloc]init];
for(NSString *path in files) {
if(![path hasSuffix:#"plist"]) {
continue;
}
NSString *fullPath = [directory stringByAppendingPathComponent:path];
NSData *data = [NSData dataWithContentsOfFile:fullPath];
IRSkillsObject *object = [NSKeyedUnarchiver unarchiveObjectWithData:data]; // get EXEC_BAD_ACCESS here...
[data release];
[characters addObject:object];
}
I get an EXEC_BAD_ACCESS error.
The IRSkillsObject conforms to the NSCoding protocol. You can see, I commented the line that I get the error on.
I am sure it's something I am doing wrong, but I just can't see it. I have tried to step through with the debugger (placing a break point in the initWithCoder: method of the object) but I don't get any errors then. In fact, it places the data in the object properly as I watch. But once it's done loading the data, it gives the error. I have tried using the retain method, but that doesn't help.
Any help that you can provide would be greatly appreciated!
You are releasing data without allocating it.
NSData *data = [NSData dataWithContentsOfFile:fullPath];
IRSkillsObject *object = [NSKeyedUnarchiver unarchiveObjectWithData:data];
[data release];
So try this:
NSData *data = [[NSData alloc] initWithContentsOfFile:fullPath];
IRSkillsObject *object = [NSKeyedUnarchiver unarchiveObjectWithData:data];
[data release];
When an EXEC_BAD_ACCESS error is found. Usually is because some data has been released but it is still needed in the code.
Maybe there is a property inside your IRSkillsObject not retained in -initWithCoder:

Copying JSON response dictionary to plist

I have a dictionary containing a JSON response and a plist file. I want to update the values in my plist file with the JSON response values. How would I do this?
this is what i did, im working on it now, but im getting there:
JSON to dictionary:
NSString *jsonString = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
//NSLog(#"%#",jsonString);
NSArray *result = [jsonString JSONValue];
for(NSDictionary *dictionary in result){
return dictionary; //if you are getting more then one row, do something here
}
Saving the dictionary:
id plist = plistDict;
NSString *errorDesc;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *plistPath = [documentsDirectory stringByAppendingPathComponent:#"Data.plist"];
NSLog(#"%#",plistPath);
NSData *xmlData;
NSString *error;
xmlData = [NSPropertyListSerialization dataFromPropertyList:plist
format:NSPropertyListXMLFormat_v1_0
errorDescription:&error];
if(xmlData) {
if ([xmlData writeToFile:plistPath atomically:YES]) {
NSLog(#"Data successfully saved.");
}else {
NSLog(#"Did not managed to save NSData.");
}
}
else {
NSLog(#"%#",errorDesc);
[error release];
}
}
if you want to update values, I would say you should open the plist, place it in a dictionary, update the value in the dictionary, and save the dictionary to the plist again.
Hope this helps.
If you are working under Mac OS X 10.7 or iOS 5, there is a Foundation class called NSJSONSerialization that will read/write JSON files. Converting JSON to plist will be as simple as: (Implying you have ARC or GC on)
NSString *infile = #"/tmp/input.json"
NSString *oufile = #"/tmp/output.plist"
[[NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:infile]
options:0
error:NULL] writeToFile:oufile
atomically:YES];
However a conversion from plist to JSON will be more troublesome since NSDate and NSData objects cannot appear in JSONs. You may need to check the contents of the file and store the NSData and NSDate in an alternate way (like NSData as Base-64 strings and NSDate as their UNIX times)