I have a plist file called playerData that includes a number object at index 0 indicating the highest level completed. After loading the view I read this object's integer value that is used throughout the game logic. If the player wins the game I would like to increment this number and write it to the plist file. here is the code I have (contained in an if statement)
levelNumber++;
NSNumber *levelNSNum = [[NSNumber alloc] initWithInteger:levelNumber];
[playerData replaceObjectAtIndex:0 withObject:levelNSNum];
[playerData writeToFile:[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:#"PlayerData.plist"] atomically:YES];
NSLog(#"%i should be written to file", levelNumber);
the log works so I know the conditions of the if statement have been met and the value is not the same as the one that was previously in the plist file, but for some reason this data is not being written over that data.
I am relatively new to this so I could be making an easy, stupid mistake I just can't seem to track down an answer. Thank you for your help!
You're trying to write to your bundle, which is read-only after the app is installed. You should write to somewhere in your app sandbox instead, such as in your Library/Application Support directory. (You can use - [NSFileManager URLsForDirectory:inDomains:] with NSApplicationSupportDirectory to find this path; be sure to create the directory before you try to write to it.)
Related
On my iOS application I'm unziping files in "app/temp" folder like this:
NSString *unzipFolder = [[CommonFunctions getCachePath] stringByAppendingPathComponent:#"/temp/"];
and once im done with it im deleting item with:
[[NSFileManager defaultManager] removeItemAtPath:unzipFolder error:&e];
Issue is that bcz im creating mulital copy of unzip files some of the files Images name are same and displaying wrong images, and i dont find error on why my deleting function does not work!
IS there any way that i can do unziping of folder on diffrent path for each message that open by user?
Thanks :)
It sounds like all you're asking is how to generate a unique name for your unzipFolder each time.
Just don't use a hardcoded name. Almost anything will do. For example:
NSString *unzipFolderTemplate = [[CommonFunctions getCachePath] stringByAppendingPathComponent:#"temp.XXXXXX"];
char *template = strdup([template fileSystemRepresentation]);
if (mkdtemp(template)) {
NSString *unzipFolder = [NSString stringWithFileSystemRepresentation:template
length:strlen(template)];
free(template);
// do the work
[[NSFileManager defaultManager] removeItemAtPath:unzipFolder error:&e];
}
The nice thing about mkdtemp is that it creates the directory for you, and guarantees there are no race conditions, somehow-left-over directories, or other problems. It's also more secure against, say, someone writing a crack or other jailbreak hack that exploits your code by predicting the path. The downside is, of course, that you have to drop down to C strings (which means an explicit free). But, as I said, there are many possibilities, and almost anything will do.
Also, note that I'm using #"temp.XXXXXX", not #"/temp.XXXXXX/". That's because -[stringByAppendingPathComponent:] already adds any necessary slashes for you (that is, in fact, the whole point of the method), and directory-creation functions don't need a trailing slash, so both of the slashes are unnecessary.
Meanwhile, I'm still a bit confused by what you're trying to do. If you need to keep a unique folder around for each message, and delete the folder when you're done with that message, and you could have multiple messages open at once, you need some way to remember which folder goes with which message.
For that, create an NSMutableDictionary somewhere, and right after the free(template) you'll want to do something like [tempFolderMap addObject:unzipFolder forKey:messageName]. Then, when closing a message, you'll do [tempFolderMap objectForKey:messageName] and use the result to the removeItemAtPath:error: message (and then you can also remove the key from tempFolderMap).
I have a text file created in documents directory with 200 logs/lines (with delimiter to separate data using a string). I want to TRANSFER first 50 records into another file and keep the remaining 150 records in the same file.
I thought of a logic, say
NSString *logString = [[NSString alloc]initWithContentsOfFile:logFilePath encoding:NSUTF8StringEncoding error:nil];
NSArray *logsArray = [logString componentsSeparatedByString:#"[;]"];
[#"" writeToFile:logFilePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
for (int i=0; i<=[logsArray count];i++){
if(i<=50){
"write to new file"
}
else{
"write to old file"
}
}
This logic will work fine. My concern is I am at a heavy risk of losing my data since I am emptying and rewriting my file at one point. I am already creating too much of files to back up my data. Is there any solution to handle this case ?
Thanks -
Arun.AR
Load all the 200 lines into an NSArray (like you do now).
Create a new file called something like "file_50_new".
Write the first 50 objects of the NSArray to "file_50_new".
Create a new file called something like "file_150_new".
Write the other 150 objects of the NSArray to "file_150_new".
Remove the original file.
Rename the new files.
This way, you always keep around the original file just until the end. If something happens (crash etc.) you always have all the lines around. They could be in different files, but you can always get them back later with a little more logic.
Actually, the writing of the last 150 objects can be done atomically by the Cocoa framework for you, but these steps are the gist of it.
What I'm doing:
I am reading some data off a file several times while my app runs. I use the following code to do so:
NSString *dataPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"data.txt"];
NSString *data = [NSString stringWithContentsOfFile:dataPath encoding:NSStringEncodingConversionExternalRepresentation error:NULL];
NSArray *components = [data componentsSeparatedByString:#"|||||"];
The first time this is called, it works as expected - I get an array of length 5, each section containing a relevant string.
What goes wrong:
This file is never modified. Yet, when I call the same procedure a second time (or third, fourth etc) I don't get the same result. Instead, an array of length 1 is returned, containing only part of the necessary data.
Why? I can't see any reason for this to go wrong... any help is much appreciated!
Since the file is in you AppBundle this means that you can't modify this file at all.
Are you sure that, where ever this code is called, the autorelease object are retained correctly?
If you call this block of code every time you want this data, it might be an idea to save the results of the first time and use that every time. This will speed things up a bit.
Turns out that the code works fine. The problem was elsewhere in my code, where I was (accidentally) accessing protected directories. As a result, iOS blocked my app from accessing any files at all :o
I've read up Apple's documentation of plist: http://developer.apple.com/library/mac/documentation/cocoa/Conceptual/PropertyLists/PropertyLists.pdf
However I've got a few questions about it:
1) When we use the [dict writeToFile:plistPath atomically:YES] API, does it overwrite the current content of the plist? It doesn't say anything in the documentation.
2) Are we supposed to actually make the plist manually in Xcode by new file->resources->property list? Or are we supposed to have this:
NSFileManager *fileManager = [NSFileManager defaultManager];
NSData *xmlData = [NSPropertyListSerialization //... a very long line here
if([fileManager fileExistsAtPath:plistPath]) {
[xmlData writeToFile:plistPath atomically:YES];
}
else {
[fileManager createFileAtPath:plistPath contents:xmlData attributes:nil];
}
3) How do we check if we've actually written data to property list? I tried products -> myapp.app -> "reveal in finder" -> right click -> show package contents, and there are some plists there, but I can't see the data being written! Is that mean I'm failed writing data to plist?
EDIT: Thanks everyone! Sorry for being silly today!
From the description of writeToFile:atomically:
If flag is YES, the dictionary is written to an auxiliary file, and then the auxiliary file is renamed to path. If flag is NO, the dictionary is written directly to path. The YES option guarantees that path, if it exists at all, won’t be corrupted even if the system should crash during writing.
Since it is written to an auxiliary file and then renamed to the specified path, I would assume that it overwrites the current content of the file.
You should use the NSFileManager to find the application's documents directory and write the plist there. You should not use a resource, as you are going to write to it during the course of executing the app.
Add some logging (e.g., NSLog(#"plist path: %#", plistPath);) to show where the plist is getting written.
1) It will overwrite the current content. If you want to append some more data to your current plist file, first read it to a dictionary and add the data and write it back.
2) You can add it manually or create it programmatically.
3) Just log the contents of the plist file after writing.
I have found several snippets of code describing how to write data to a user's application Documents folder. However, when I try this out in the iPhone simulator, no files get created. I called
[NSFileManager isWritbleAtPath:<my document folder>]
and it returned 0 (false). Do I need to make this folder explicitly writable, and if so, how do I do it?
The iPhone simulator should be able to write to the entire disk. My app routinely dumps test files to the root level of my boot volume (using [NSData's writeToPath:#"/test.jpg" atomically:NO]).
Are you sure that you've correctly determined the path to the documents folder? You need to expand the tilde in the path. Here's the code my app uses to put things in the documents folder. I don't think there's any more setup involved!
brushesDir = [[#"~/Documents/BrushPacks/" stringByExpandingTildeInPath] retain];
// create brush packs folder if it does not exist
if (![[NSFileManager defaultManager] fileExistsAtPath: brushesDir])
[[NSFileManager defaultManager] createDirectoryAtPath:brushesDir withIntermediateDirectories:YES attributes:nil error:nil];
NSLog(#"writable: %d", [[NSFileManager defaultManager] isWritableFileAtPath:NSHomeDirectory()]);
This prints 1 on the console.
Did you mean to call the method isWritableAtPath or isWritableFileAtPath ? And did you mean to call it on the class itself, or on a (default) instance of it?
Thanks for the pointers. So after a toiling through a few documents, I found the thing I was doing wrong: trying to save an NSArray that wasn't composed of basic datatypes such as NSDictionary, NSArray, or NSString. I was trying to save an array of MPMediaItems (from the MediaKit Framework in SDK 3.0+).
I had a trivial issue with the file writing to NSBundle. I had a requirement where a text file needs to be updated with the server as soon as app launches and it worked well with the simulator but not with the device. I later found out that we don't have write permission with NSBundle. Copying the file into Documents directory from NSBundle and using for my purpose solved my problem. I use :
[myPlistData writeToFile:fileName atomically:NO];