Write NSMutableArray to plist not working - objective-c

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.

Related

Write an NSMutableArray to a file and load it back

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.

Create plist file via code

I want to create a plist file like this:
in code, and then it will be in the Caches folder.
I already know how to fetch the data.
You can do it like this:
//Create a Mutant Dictionary
NSMutableDictionary *theMutantDict = [[NSMutableDictionary alloc] init];
//Fill it with data
[theMutantDict setObject:#"John" forKey:#"Name"];
[theMutantDict setObject:#"Doe" forKey:#"Lastname"];
//Then search for cache dir
NSString *libraryDir = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
NSUserDomainMask, YES) objectAtIndex:0];
NSString *cacheDir = [libraryDir stringByAppendingPathComponent:#"Caches"];
//Then write the file
NSString *filePath = [cacheDir stringByAppendingString:#"/TheFile.plist"];
[theMutantDict writeToFile:filePath atomically:YES];
Just use the writeToFile-method of NSArray to store the array in a plist and arrayWithContentsOfFile to load it.
[self.array writeToFile:path atomically:YES];
self.array = [[NSArray arrayWithContentsOfFile:path];
// or in case you need to add/remove objects (NSMutableArray):
self.array = [[[NSArray arrayWithContentsOfFile:path] mutableCopy] autorelease];
If you got your data stored in an NSArray it's as easy as this:
// filling array with data ...
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachePath = [paths objectAtIndex:0];
NSString *plistPath = [cachePath stringByAppendingPathComponent:#"my_nsarray_data.plist"];
[klasserArray writeToFile:plistPath atomically:YES];
// other stuff ...

How can I read user input text from a textfield and write it to a text file in Xcode?

Is there a way to write a string , which is read from text field, to a .txt file?
NSString *input = [textfield text];
NSString *path = #"myText.txt";
[input writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:NULL];
Yes, ultimately that is the way; but there's a number of other steps you do in order to get from start to finish (you need to have textfield connected to an IBOutlet or in some other way accessible; the path where myText.txt is writing to needs to be writable and therefore you'd probably need to have a longer, more precise path than just the filename; you'd probably need to also send in an actual error parameter that could be set so you could look at the errors being returned when the writeToFile call fails the first few times you run this code).
You will need to define a path, basically where to save the file.
This code will find the Documents folder
NSArray *path = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirPath = [path objectAtIndex:0];
Here is the code I use to read and write files.
-(void)writeFileToDisk:(id)data
{
NSArray *path = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirPath = [path objectAtIndex:0];
NSString *fileName = #"MyFileName.txt";
NSString *fileAndPath = [documentDirPath stringByAppendingPathComponent:fileName];
[data writeToFile:fileAndPath atomically:YES];
}
-(void)readFileFromDisk
{
NSArray *path = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirPath = [path objectAtIndex:0];
NSString *fileName = #"MyFileName.txt";
NSString *fileAndPath = [documentDirPath stringByAppendingPathComponent:fileName];
NSArray *array = [[NSArray alloc] initWithContentsOfFile:fileAndPath];
NSLog(#"%#",array);
[array release];
}

NSMutable Array to XML file

I have an NSMutableArray of custom objects called Clip. Each clip Object has some NSString properties (clipName,tcIn, tcOut, file path and so on..). How do I write an xml File on disk where each clip in the array is a node of the xml file and its properties are elements of the node?
Thanks in advance
Dario
I think this is what should do you:
//Create xml string
NSMutableString *xmlString = [[NSMutableString alloc] init];
//Add all the data to the string
[xmlString appendFormat:#"<clips>"];
for (Clip *c in clipsArray)
{
[xmlString appendFormat:#"\n\t<clip>"];
[xmlString appendFormat:#"\n\t\t<clipName>%#</clipName>", c.clipName];
[xmlString appendFormat:#"\n\t\t<tcIn>%#</tcIn>", c.tcIn];
...
[xmlString appendFormat:#"</clip>"];
}
[xmlString appendFormat:#"</clips>"];
//Create a variable to represent the filepath of the outputted file
//This is taken from some code which saves to an iPhone app's documents directory, it might not be ideal
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *finalPath = [documentsDirectory stringByAppendingPathComponent:#"output.xml"];
//Actually write the information
NSData *data = [xmlString dataUsingEncoding:NSUTF8StringEncoding];
[[NSFileManager defaultManager] createFileAtPath:finalPath contents:data attributes:nil];

writeToFile:atomically only works first time

When my app starts, looks if a plist exists, if it doesn't it creates it.
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"history.plist"];
success = [fileManager fileExistsAtPath:filePath];
if (success) {
NSArray *arr = [[NSArray alloc] initWithContentsOfFile:[documentsDirectory stringByAppendingPathComponent:#"history.plist"]];
for (NSDictionary *par in arr) {
[self.history addObject:[[Paradero alloc] initWithDictionary:par]];
}
} else {
[fileManager createFileAtPath:filePath contents:nil attributes:nil];
}
Now, when the user closes the app, to provide persistance I save the data to that file
- (void)applicationDidEnterBackground:(UIApplication *)application {
/*
Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
If your application supports background execution, called instead of applicationWillTerminate: when the user quits.
*/
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"history.plist"];
NSLog(#"%#", filePath);
NSMutableArray *temparray = [[NSMutableArray alloc] initWithCapacity:5];
for (Paradero *parada in self.history) {
[temparray addObject:[parada asDictionary]];
}
NSLog(#"%#", temparray);
NSLog(#"%c",[temparray writeToFile:filePath atomically:NO]);
NSLog(#"yei");
}
The object is converted to a Dictionary with NSString, NSArrays so it can be saved to the file. The first time this ever happens, that means, the file was created, nothing on it, it works just fine, but when is time to save new data, it fails, no reason given.
Solved.
For the record:
The first issue was that i was testing on iOS5 Simulator, well, i don't know how to fix it there yet.
The real issue was because there was some NSNull around, so, I just needed to turn them into empty strings.
Remember to check for NSNulls, they are really a pain if they turn out from a web service.