writeToFile:atomically only works first time - objective-c

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.

Related

Objective-C: Saving Archived Data to File Crashes

my inquiry is this: I am saving and loading custom objects to file. Loading and creating files with the data works correctly, however, after a file has loaded, saving again crashes. Long story short, saving a file that has been loaded causes a crash upon saving.
The crash sends me to my custom objects m file:
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeInteger:*(time) forKey:#"time"];
[encoder encodeInteger:*(location) forKey:#"location"];
}
//====
Loading the data from file:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents folder
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:fileName];
initWithContentsOfFile:filePath];
globals.mainData = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
//====
Saving to current file
My breakpoint stop me at: NSData *data = ... //and then crashes
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents folder
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:currentFileName];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:globals.mainData]; //crashes here
BOOL saved = [data writeToFile:filePath options:NSDataWritingAtomic error:nil];
if (saved) {
NSLog(#"Saved %#", currentFileName);
}else{
NSLog(#"Error - code 2 - Failure to save data");
}
Any advice would be awesome and much appreciated!

Objective-c load and write plist - error

Hi I'm practicing with plists and I learned that there are 2 different ways to load them
FIRST METHOD:
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documents = [path lastObject];
NSString *filePath = [documents stringByAppendingPathComponent:#"test.plist"];
self.array = [NSArray arrayWithContentsOfFile:filePath];
SECOND METHOD:
NSString *filePath = [[NSBundle mainBundle]pathForResource:#"Ingredients" ofType:#"plist"];
self.array = [NSArray arrayWithContentsOfFile:filePath];
I don't understand clearly which way it's best... but I noticed that if I use the second one, I can't write in the plist. can anyone tell me more about it? which is the best and correct way? What's the difference?
i'm doing some tests and i have some code working only with one method...
//using this code the nslog will print null
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *filePath = [documentsPath stringByAppendingPathComponent:#"Ingredients.plist"];
ingredients = [NSMutableArray arrayWithContentsOfFile:filePath];
NSLog(#"ingredients:%#", self.ingredients);
//using this code the nslog will print the content of the array
NSString *filePath = [[NSBundle mainBundle]pathForResource:#"Ingredients" ofType:#"plist"];
ingredients = [NSMutableArray arrayWithContentsOfFile:filePath];
NSLog(#"Authors:%#", self.ingredients);
First Method
Your app only (on a non-jailbroken device) runs in a "sandboxed" environment. This means that it can only access files and directories within its own contents. For example Documents and Library.
Reference iOS Application Programming Guide.
To access the Documents directory of your applications sandbox, you can use the following:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
This Documents directory allows you to store files and subdirectories your app creates or may need.
To access files in the Library directory of your apps sandbox use (in place of pathsabove):
[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0]
Second Method
The Second Method is used to write the file in the Apps main bundle.
The main bundle is the bundle that contains the code and resources for the running application. If you are an application developer, this is the most commonly used bundle. The main bundle is also the easiest to retrieve because it does not require you to provide any information.
It is better to copy the file from App Main Bundle to App Document Directory and then use the document directories path to read/write file.
If you are using the first method you need to copy the file from your main resources to the Documents Directory.
Code to Copy file from app bundle to App's Document Directory
#define FILE_NAME #"sample.plist"
// Function to create a writable copy of the bundled file in the application Documents directory.
- (void)createCopyOfFileIfNeeded {
// First, test for existence.
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:FILE_NAME];
success = [fileManager fileExistsAtPath:filePath];
if (success){
return;
}
// The writable file does not exist, so copy the default to the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:FILE_NAME];
success = [fileManager copyItemAtPath:defaultDBPath toPath:filePath error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable file with message '%#'.", [error localizedDescription]);
}
}
Sample Code Dropbox Link

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.

How to save text/data in cocoa/xcode to a file?

i'm sorry for the obvious question, but is there something i should be careful about when writing data to a file, because my program can read the data it just wrote, while the program is running, but the moment i stop it, the files become empty
tried using NSFileHandle, in order to write data with it, and close the file later, didn't help... currently, i'm using:
NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:newArray];
[encodedObject writeToFile:string atomically:YES];
and no matter what i do, i can't get the simplest NSString to stay in the file permanently
what do i do?
Thanks #LetzFlow, but it didn't solve it just yet.
Now, i'm using:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
string = [NSString stringWithFormat:#"%d.rtf",fileName];
NSString *file = [documentsDirectory stringByAppendingPathComponent:string];
//NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:file];
NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:newArray];
[encodedObject writeToFile:file atomically:YES];
//[fileHandle writeData:encodedObject];
//[fileHandle closeFile];
NSLog(#"%#",[NSString stringWithContentsOfFile:file encoding:NSStringEncodingConversionAllowLossy error:nil]);
to serialize an array of objects, and it the NSLog shows a valid array. Yet, when I look at the file later, or try to unserialize the array (like this):
NSString *stringX = [NSString stringWithFormat:#"%d.rtf",fileName];
NSData *encodedObjectX = [NSData dataWithContentsOfFile:stringX];
NSArray *newArrayX = [NSKeyedUnarchiver unarchiveObjectWithData:encodedObjectX];
TurnigButton *button = [[newArrayX objectAtIndex:1] objectAtIndex:5];
NSLog(#"%d", button.idNum);
it just prints (null). (when they execute one after the other in a single run, it unserializes just fine)
I appreciate the help.
The question is WHERE do you write your file? Because you are not allowed to write files all over the system in iOS.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
The documents directory is usually a place were you can write files, so you might want to give that a try.

objective-c simple nsarray writeToFile issue

[wordlist writeToFile:[[NSBundle mainBundle]
pathForResource:#"wordlist" ofType:#"txt"] atomically: YES];
wordlist is a NSMutableArray, and the rest you know what.
The problem is that nothing is saved in wordlist.txt when I run the codes in Xcode. In front of this code nslog shows that there are 4 objects in wordlist. How come?
Edit:
Right!
These codes work!:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *yourArrayFileName = [documentsDirectory stringByAppendingPathComponent:#"wordlist.txt"];
[wordlist writeToFile:yourArrayFileName atomically:YES];
NSLog (#"%#", yourArrayFileName);
Edit 29 august 2011.
NSString *documentsDirectory = #"/Users/YOURNAME/Desktop/";
NSString *appFile = [documentsDirectory stringByAppendingPathComponent:#"norsk.txt"];
NSString *content = #"æ ø å";
BOOL ok = [content writeToFile:appFile atomically:YES encoding:NSUnicodeStringEncoding error:nil];
if (!ok) {
NSLog(#"Error writing file !");
}
also works!
You can't write in you app's bundle.
You need to write in the user's directory. You can know more about it in this question.
There's three directory you can write in
NSDocumentDirectory : the document directory. The user will be able to see these file. This should be for user files only. Backed up by iTunes.
NSCachesDirectory : the cache directory is where you place things you app may need in the future, but that you can reconstruct. Not backed up by iTunes.
NSApplicationSupportDirectory : the application support is where you place your essential files for your application. Backed up by iTunes.