Does an iOS app have write access inside its bundle? - ios

I save some run-time generated files inside the .app bundle of my iOS app. In the simulator it works fine, in the device it crashes:
Could create output files in the given shader cache path
'/var/mobile/Applications/CB064997-B40E-4FE3-9834-B3217CE33489/SimedTest.app/Ogre3D/assets/RTShaderLib/cache/
Is there a good overview of where I should and shouldn't put files - how to use Documents, Library and tmp, etc?
To clarify, these are files created at startup which pre-calculate some data to save time. IF they are not present they get created so it's fine they are deleted, but not while the app is running.

The bundle is read-only. You don't want to mess around with it for two reasons:
Code Signing: the signature is verified by against the contents of the bundle; if you mess around with the bundle, you break the signature.
App Updates: updates work by replacing the entire app bundle with a newly downloaded one; any changes you make will get lost.
Where you should save stuff:
Documents: if you want it to persist and be backed up
Library/Caches: if you just want to cache downloaded data, like profile pics; will be auto deleted by the system if it is low on room unless you specify with a special do-not-delete flag.
tmp: temporary files, deleted when your app is not running
For a full explanation check out File System Programming Guide and QA1719.

No, every time you change your bundle you invalidate your signature.
If you want to write files you`l need to write in the best folder depending on what you want to do with that file.
Documents folder for long duration files
Cache for small operations
and so on
EDIT
To get the path you`ll need something like this:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"filename.ext"];
With this path you can write or read like this:
write:
NSString *content = #"One\nTwo\nThree\nFour\nFive";
[content writeToFile:fileName atomically:NO encoding:NSStringEncodingConversionAllowLossy error:nil];
read:
NSString *content = [[NSString alloc] initWithContentsOfFile:fileName usedEncoding:nil error:nil];

Related

Write to a file. Functional in the simulator and not functional on a real iphone [duplicate]

I am trying to take content from one file and write it into another. I am reading fine, but I am not able to write it into another file.
I have a database of words. I want to separate the words into different files based on the number of letters. All four letter words go into one file, and so on. I added a txt file called "4letter" into my resources and the following is my code:
NSError *error;
//READ
NSString *dbFile = [[NSBundle mainBundle] pathForResource:#"words" ofType:#"txt"];
NSString *test = [NSString stringWithContentsOfFile:dbFile encoding:NSUTF8StringEncoding error:&error];
//convert from string to array
NSArray *lines = [test componentsSeparatedByString:#"\n"];
NSFileHandle *logFile = nil;
logFile = [NSFileHandle fileHandleForWritingAtPath:[[NSBundle mainBundle] pathForResource:#"4letter" ofType:#"txt"]];
//Test if write works
for (int i=0; i<5; i++)
{
NSString *randomAnagram = [[lines objectAtIndex:i] lowercaseString];
[logFile writeData: [randomAnagram dataUsingEncoding: NSNEXTSTEPStringEncoding]];
}
In iOS, you can't write into a file in your app's bundle -- the entire bundle is read-only. Use a path into the Documents folder instead.
See special File System Programming Guide for better understnading.
In iOS, you can't write into a file in your app's bundle -- the entire bundle is read-only.
Consider reading iOS Data Storage Guidelines to better understand the purpose of directories below, in context of iCloud backup.
<Application_Home>/AppName.app
This is the bundle directory containing the app itself. Do not write
anything to this directory. To prevent tampering, the bundle directory
is signed at installation time. Writing to this directory changes the
signature and prevents your app from launching again.
<Application_Home>/Documents/
Use this directory to store critical user documents and app data
files. Critical data is any data that cannot be recreated by your app,
such as user-generated content. The contents of this directory can be
made available to the user through file sharing. The contents of this
directory are backed up by iTunes.
<Application_Home>/Library/
This directory is the top-level directory for files that are not user
data files. You typically put files in one of several standard
subdirectories but you can also create custom subdirectories for files
you want backed up but not exposed to the user. You should not use
this directory for user data files. The contents of this directory
(with the exception of the Caches subdirectory) are backed up by
iTunes. For additional information about the Library directory, see
“The Library Directory Stores App-Specific Files.”
See full list (tmp/, Documents/Inbox) in iOS Standard Directories: Where Files Reside
UPDATE
I use NSFileManager method URLForDirectory:inDomain:appropriateForURL:create:error:
Like Caleb said, you can't write to your app's directory, but you can write to your app's Documents folder. You can get it like this:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
Your app's bundle is read-only. There is two ways I could see:
1) Write in documents folder:
NSArray *pathList = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path =  [myPathList  objectAtIndex:0];
2) Use sqlite database. This is the same as 1 (you must save db in documents anyway), but you're using sqlite database. I think this is better than a lot of txt and plist files: here's a tutorial on the topic.
I use the following code :
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *appFile = [documentsDirectory stringByAppendingPathComponent:#"set.txt"];
NSString *data=#"Kostas";
[data writeToFile:appFile atomically:YES];
NSString *myData = [NSString stringWithContentsOfFile:appFile];
NSLog(#"Data : %# ",myData);

Writing big file on IOS [duplicate]

I save some run-time generated files inside the .app bundle of my iOS app. In the simulator it works fine, in the device it crashes:
Could create output files in the given shader cache path
'/var/mobile/Applications/CB064997-B40E-4FE3-9834-B3217CE33489/SimedTest.app/Ogre3D/assets/RTShaderLib/cache/
Is there a good overview of where I should and shouldn't put files - how to use Documents, Library and tmp, etc?
To clarify, these are files created at startup which pre-calculate some data to save time. IF they are not present they get created so it's fine they are deleted, but not while the app is running.
The bundle is read-only. You don't want to mess around with it for two reasons:
Code Signing: the signature is verified by against the contents of the bundle; if you mess around with the bundle, you break the signature.
App Updates: updates work by replacing the entire app bundle with a newly downloaded one; any changes you make will get lost.
Where you should save stuff:
Documents: if you want it to persist and be backed up
Library/Caches: if you just want to cache downloaded data, like profile pics; will be auto deleted by the system if it is low on room unless you specify with a special do-not-delete flag.
tmp: temporary files, deleted when your app is not running
For a full explanation check out File System Programming Guide and QA1719.
No, every time you change your bundle you invalidate your signature.
If you want to write files you`l need to write in the best folder depending on what you want to do with that file.
Documents folder for long duration files
Cache for small operations
and so on
EDIT
To get the path you`ll need something like this:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"filename.ext"];
With this path you can write or read like this:
write:
NSString *content = #"One\nTwo\nThree\nFour\nFive";
[content writeToFile:fileName atomically:NO encoding:NSStringEncodingConversionAllowLossy error:nil];
read:
NSString *content = [[NSString alloc] initWithContentsOfFile:fileName usedEncoding:nil error:nil];

Can't modify file programmatically objective-c [duplicate]

I am trying to take content from one file and write it into another. I am reading fine, but I am not able to write it into another file.
I have a database of words. I want to separate the words into different files based on the number of letters. All four letter words go into one file, and so on. I added a txt file called "4letter" into my resources and the following is my code:
NSError *error;
//READ
NSString *dbFile = [[NSBundle mainBundle] pathForResource:#"words" ofType:#"txt"];
NSString *test = [NSString stringWithContentsOfFile:dbFile encoding:NSUTF8StringEncoding error:&error];
//convert from string to array
NSArray *lines = [test componentsSeparatedByString:#"\n"];
NSFileHandle *logFile = nil;
logFile = [NSFileHandle fileHandleForWritingAtPath:[[NSBundle mainBundle] pathForResource:#"4letter" ofType:#"txt"]];
//Test if write works
for (int i=0; i<5; i++)
{
NSString *randomAnagram = [[lines objectAtIndex:i] lowercaseString];
[logFile writeData: [randomAnagram dataUsingEncoding: NSNEXTSTEPStringEncoding]];
}
In iOS, you can't write into a file in your app's bundle -- the entire bundle is read-only. Use a path into the Documents folder instead.
See special File System Programming Guide for better understnading.
In iOS, you can't write into a file in your app's bundle -- the entire bundle is read-only.
Consider reading iOS Data Storage Guidelines to better understand the purpose of directories below, in context of iCloud backup.
<Application_Home>/AppName.app
This is the bundle directory containing the app itself. Do not write
anything to this directory. To prevent tampering, the bundle directory
is signed at installation time. Writing to this directory changes the
signature and prevents your app from launching again.
<Application_Home>/Documents/
Use this directory to store critical user documents and app data
files. Critical data is any data that cannot be recreated by your app,
such as user-generated content. The contents of this directory can be
made available to the user through file sharing. The contents of this
directory are backed up by iTunes.
<Application_Home>/Library/
This directory is the top-level directory for files that are not user
data files. You typically put files in one of several standard
subdirectories but you can also create custom subdirectories for files
you want backed up but not exposed to the user. You should not use
this directory for user data files. The contents of this directory
(with the exception of the Caches subdirectory) are backed up by
iTunes. For additional information about the Library directory, see
“The Library Directory Stores App-Specific Files.”
See full list (tmp/, Documents/Inbox) in iOS Standard Directories: Where Files Reside
UPDATE
I use NSFileManager method URLForDirectory:inDomain:appropriateForURL:create:error:
Like Caleb said, you can't write to your app's directory, but you can write to your app's Documents folder. You can get it like this:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
Your app's bundle is read-only. There is two ways I could see:
1) Write in documents folder:
NSArray *pathList = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path =  [myPathList  objectAtIndex:0];
2) Use sqlite database. This is the same as 1 (you must save db in documents anyway), but you're using sqlite database. I think this is better than a lot of txt and plist files: here's a tutorial on the topic.
I use the following code :
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *appFile = [documentsDirectory stringByAppendingPathComponent:#"set.txt"];
NSString *data=#"Kostas";
[data writeToFile:appFile atomically:YES];
NSString *myData = [NSString stringWithContentsOfFile:appFile];
NSLog(#"Data : %# ",myData);

Default files in iOS application sandbox

I am creating a quiz app for iPhone. I store all the questions in an NSArray that is loaded from a .xml file every time the app is opened. That file will be the same accross all "instances" of the app, and it will never be changed by the user.
I could hard-code all the questions with code, but I found it more elegant to have it in a file that is easily edited by any text editor.
But how do I give each app "instance" of the app a copy of that file? That file should be bundled along with the rest of the application files.
This is the code I use so far (in app delegate).
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES);
NSString *path = documentPaths[0];
path = [path stringByAppendingPathComponent:#"questions.archive"];
NSArray *fileQuestions = [[NSArray alloc] initWithContentsOfFile:path];
if (!fileQuestions)
{
// Code to add three default questions…
[fileQuestions writeToFile:path
atomically:YES];
}
// And here the "file questions" are transformed to instances of my question class
No need to try to store the file in Documents directory. Just store it right there in the app bundle itself. This works exactly like an image file, a sound file, or any other resource. Make the file part of the project; make sure it is part of the app target, so that it will be copied into the app bundle as part of the build process. In the running app, get its path with [[NSBundle mainBundle] pathForResource:ofType:], and read it as you would any file of this type.

How to hardcode a text file into iOS app

Currently using:
NSString* path = [[NSBundle mainBundle] pathForResource:#"filename"
ofType:#"txt"];
This only works however after I manually add the file into the application bundle.
I can use the documents directory but that's even worse.
After resetting the sim, the file goes away. How do I get it to stay?
How would I write out the file from somewhere to there? Like from a file in source? I don't want to alloc a several megabyte NSString object.
Credit goes to Lyle42 on freenode irc (#iphonedev):
I wasn't aware this field even existed. By adding any file into the copy files build phase (under build phases), they persist across builds.
Then this code:
NSString *_filePath = [[NSBundle mainBundle] pathForResource:#"filename" ofType:#"txt"];
NSLog(#"%#",_filePath);
NSData *_binary = [NSData dataWithContentsOfFile:_filePath];
NSString *_fileContents = [[NSString alloc] initWithData:_binary encoding:NSUTF8StringEncoding];
Works just great. (loads contents of file into an NSString).
You can use this to get your path for the file
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [documentPaths objectAtIndex:0];
filePath = [path stringByAppendingPathComponent:file_name];
you can get the file from your app bundle using
NSString *pathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:file_name];
I can use the documents directory but that's even worse.
Not really. The documents directory is persistent, and the recommended directory for essential documents created by your application. In addition, it's backed up by iTunes, and can be specified for iCloud backup. The app bundle just can't compete with that!
After resetting the sim, the file goes away. How do I get it to stay?
You can't, once the simulator has been "reset" (and I mean a hard wipe of all data and apps), nothing is saved. In between test builds, however, data copied out of the app bundle should survive unless you're storing things in /tmp. In addition, objects in the bundle are "refreshed" (recopied) in the build phase, meaning your resource isn't wiped out, merely reset to the one specified in your project.
How would I write out the file from somewhere to there? Like from a
file in source? I don't want to alloc a several megabyte NSString
object.
To read the file into memory, it would have to turn into a "several megabyte NSString" eventually, but if you really want to add a little IO to your plans for a massive string object, have a look at NSFileManager.

Resources