Write an NSMutableArray to a file and load it back - objective-c

I'm doing some exercises about writing to and loading from a file.
I've created an NSString, then written it to a file, and then loaded an NSString again. Simple.
How can I do this with an NSMutableArray of NSStrings, or better an NSMutableArray of my own class?
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{
#autoreleasepool {
// insert code here...
//write a NSString to a file
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"file.txt"];
NSString *str = #"hello world";
NSArray *myarray = [[NSArray alloc]initWithObjects:#"ola",#"alo",#"hello",#"hola", nil];
[str writeToFile:filePath atomically:TRUE encoding:NSUTF8StringEncoding error:NULL];
//load NSString from a file
NSArray *paths2 = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory2 = [paths2 objectAtIndex:0];
NSString *filePath2 = [documentsDirectory2 stringByAppendingPathComponent:#"file.txt"];
NSString *str2 = [NSString stringWithContentsOfFile:filePath2 encoding:NSUTF8StringEncoding error:NULL];
NSLog(#"str2: %#",str2);
}
return 0;
}
Printed: str2: hello world

If you want to write your array as a plist, you can
// save it
NSArray *myarray = #[#"ola",#"alo",#"hello",#"hola"];
BOOL success = [myarray writeToFile:path atomically:YES];
NSAssert(success, #"writeToFile failed");
// load it
NSArray *array2 = [NSArray arrayWithContentsOfFile:path];
NSAssert(array2, #"arrayWithContentsOfFile failed");
For more information, see Using Objective-C Methods to Read and Write Property-List Data in the Property List Programming Guide.
But, f you want to preserve the mutability/immutability (i.e. the precise object types) of your objects, as well as open up the possibility of saving a wider array of object types, you might want to use an archive rather than a plist:
NSMutableString *str = [NSMutableString stringWithString:#"hello world"];
NSMutableArray *myarray = [[NSMutableArray alloc] initWithObjects:str, #"alo", #"hello", #"hola", nil];
//save it
BOOL success = [NSKeyedArchiver archiveRootObject:myarray toFile:path];
NSAssert(success, #"archiveRootObject failed");
//load NSString from a file
NSMutableArray *array2 = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
NSAssert(array2, #"unarchiveObjectWithFile failed");
While I illustrate the technique with an array, it works with any object that conforms to NSCoding (which includes many of the basic Cocoa classes like strings, arrays, dictionaries, NSNumber, etc.). If you want to make your own classes work with NSKeyedArchiver, then you must make them conform to NSCoding, too. For more information, see the Archives and Serializations Programming Guide.

This document at Apple walks you through the process.

Related

cocoa and objective c save, edit and load files

hello I am trying to create a text file that will then store some data
- (IBAction)saveUser:(id)sender{
NSString *name = [nameField stringValue];
NSString *weight = [weightField stringValue];
NSDate *date = [datePick dateValue];
}
I want to be able to create a file that stores the following information and uses the name field as the name of the file. I also want to be able to load the file and read that data from it
Any help is appreciated
Thanking You
This is quite simple. But you might want to look into NSMutableDictionary instead. Both NSString and Dictionary have methods writeToFile:#"filename.txt".
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setValue:[nameField stringValue] forKey:#"name"];
[dict setValue:[weightField stringValue] forKey:#"weight"];
[dict setValue:date forKey:#"date"];
[dict writeToFile:name atomically:YES];
you read from the file the same way,
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithContentsOfFile:name];
As simple as it gets
I think you may get some informations from class NSFileManager, such as like this,
[fileManager createFileAtPath:filePath contents:contentData attributes:NULL];
the contentData is NSData, and it includes the content of what you want to save, the filename is in filePath that you need to set.
Thanks!

Objective-C: writing values to a specific index of an Array inside a .plist

I understand I can for instance write a value to a .plist file as such
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"stored" ofType:#"plist"];
NSString *comment = #"this is a comment";
[comment writeToFile:filePath atomically:YES];
But If i had for say an Array inside my .plist (gameArray) and I'd like to white comment into a particular index of my array i.e. gameArray[4] ; how would I do this ?
allow me to clarify
I have a plist: stored.plist
inside my plist there is an array gameArray
i would like to update specific indexes of gameArray inside the plist
is this possible ?
You cannot update and save data in application's main bundle instead you have to do in document directory or other directory like this:
NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *plistFilePath = [documentsDirectory stringByAppendingPathComponent:#"stored.plist"];
if([[NSFileManager defaultManager] fileExistsAtPAth:plistFilePath])
{//already exits
NSMutableArray *data = [NSMutableArray arrayWithContentsOfFile:plistFilePath];
//update your array here
NSString *comment = #"this is a comment";
[data replaceObjectAtIndex:4 withObject:comment];
//write file here
[data writeToFile:plistFilePath atomically:YES];
}
else{ //firstly take content from plist and then write file document directory
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"stored" ofType:#"plist"];
NSMutableArray *data = [NSMutableArray arrayWithContentsOfFile:plistPath];
//update your array here
NSString *comment = #"this is a comment";
[data replaceObjectAtIndex:4 withObject:comment];
//write file here
[data writeToFile:plistFilePath atomically:YES];
}
Assuming the contents of 'stored.plist' is an array, you need to instantiate a mutable array from the path:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"stored" ofType:#"plist"];
NSMutableArray *array = [NSMutableArray arrayWithContentsOfFile:filePath];
NSString *comment = #"this is a comment";
// inserting a new object:
[array insertObject:comment atIndex:4];
// replacing an existing object:
// classic obj-c syntax
[array replaceObjectAtIndex:4 withObject:4];
// obj-c literal syntax:
array[4] = comment;
// Cannot save to plist inside your document bundle.
// Save a copy inside ~/Library/Application Support
NSURL *documentsURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask] objectAtIndex:0];
NSURL *arrayURL = [documentsURL URLByAppendingPathComponent:[filePath lastPathComponent]];
[array writeToURL:arrayURL atomically:NO];

Write NSMutableArray to plist not working

I am using plists to save/load an NSMutableArray,
the code:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *prsPath = [documentsDirectory stringByAppendingPathComponent:#"records.plist"];
prs = [[NSMutableArray alloc] initWithContentsOfFile:prsPath];
when I am using the last sentence of code somewhere else in my code it says: "prsPath" undeclared. (I am loading my code in ViewDidLoad) When I add an Object it doesn't save it, it doesn't even show up. (Loading the last sentence on add)
I'm using this method and it's work 100%
- (void) writeToPlist: (NSString*)fileName withData:(NSMutableArray *)data
{
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *finalPath = [documentsDirectory stringByAppendingPathComponent:fileName];
[data writeToFile:finalPath atomically: YES];
/* This would change the firmware version in the plist to 1.1.1 by initing the NSDictionary with the plist, then changing the value of the string in the key "ProductVersion" to what you specified */
}
and this method for reading from plist file:
- (NSMutableArray *) readFromPlist: (NSString *)fileName {
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *finalPath = [documentsDirectory stringByAppendingPathComponent:fileName];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:finalPath];
if (fileExists) {
NSMutableArray *arr = [[NSMutableArray alloc] initWithContentsOfFile:finalPath];
return arr;
} else {
return nil;
}
}
Hope it can help you.
[[NSMutableArray alloc] initWithContentsOfFile:prsPath] will load a plist ant initialize an array with it. Does you plist exist at that path already? You might also want to log the prsPath to see if it's correct.
Usually you would first check to see if a plist exists at the path by calling [[NSFileManager defaultManager] fileExistsAtPath:prsPath]. If it does not, you initialize an empty array.
Later you save it by calling [prs writeToFile:prsPath atomically:YES].
Note that you can't initialize NSMutableArrays from plists. Arrays and dictionaries loaded from plists are always immutable. You would have to first load the plist into an NSArray and then initialize an NSMutableArray from that NSArray.

Creating new dictionary when saving data to plist

So I am saving 3 NSStrings from 3 UITextFields to a property list. This works fine, but everytime I save something new, the app overwrites the data that was saved before. So basically there is only 1 Dictionary used, but i want the app to create a new dictionary everytime i save something new, so that no data gets deleted. I have no Idea how i could do this, so please help me!! :)
Code:
NSMutableArray *array = [NSMutableArray arrayWithCapacity:3];
NSArray *keys = [NSArray arrayWithObjects:#"1",#"2",#"3", nil];
[array addObject:[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[NSString stringWithFormat:#"%#",lab.text],[NSString stringWithFormat:#"%#",lab1.text],[NSString stringWithFormat:#"%#",lab2.text], nil] forKeys:keys]];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"data.plist"];
[array writeToFile:path atomically:YES];
Use a NSMutableArray to which you add each new dictionary object and then write that array to data.plist.
Is there any reason why you alloc the array with capacity? I would just use [[NSMutableArray alloc] init], then add your objects.
Also, I had trouble saving NSMutableDictionaries in the NSUserDefaults, so what I ended up doing was just saving the dictionary to a file with
[dict writeToFile:filePath atomically:NO];
and initWithContentsOfFile or initWithContentsOfURL depending if I wanted to load a local or Internet file.
I should add, you can writeToFile, initWithContentsOf* for NSMutableArray as well.
Ok I've got it:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"data.plist"];
NSLog(#"path='%#'",path);
NSFileManager *nfm = [[NSFileManager alloc] init];
if([nfm fileExistsAtPath:path])
{
// if file exists, get its contents, add more entries and write back
NSMutableArray *array = [[NSMutableArray alloc] initWithContentsOfFile:path];
NSArray *keys = [NSArray arrayWithObjects:#"4",#"5",#"6",nil];
[array addObject:[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[NSString stringWithFormat:#"%#",lab.text],[NSString stringWithFormat:#"%#",lab1.text],[NSString stringWithFormat:#"%#",lab2.text], nil] forKeys:keys]];
NSLog(#"modified array=%#",array);
BOOL ok = [array writeToFile:path atomically:YES];
if(!ok){
NSLog(#"Unable to write appended file");
return;
}
} else {
// if file doesn't exist, create a new one
NSMutableArray *array = [[NSMutableArray alloc] init];
NSArray *keys = [NSArray arrayWithObjects:#"1",#"2",#"3",nil];
[array addObject:[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[NSString stringWithFormat:#"%#",lab.text],[NSString stringWithFormat:#"%#",lab1.text],[NSString stringWithFormat:#"%#",lab2.text], nil] forKeys:keys]];
NSLog(#"new array=%#",array);
BOOL ok = [array writeToFile:path atomically:YES];
if(!ok){
NSLog(#"Unable to write new file");
return;
}
}

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)