Why can't files in my iPhone NSBundle folder be deleted? - iphone

I'm having trouble figuring out why files in my iPhone app seem to persist, even when I've deleted them. Here's the code that's giving me trouble:
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
NSString *folderPath = [bundlePath stringByAppendingPathComponent:#"filefolder"];
NSArray *fileNames = [fileManager contentsOfDirectoryAtPath:folderPath error:NULL];
This code is supposed to look at the folder "filefolder" and read its contents into fileNames. When I run this app the very first time, it will do this. But if I change the contents of filefolder (for instance if I add or delete files) and I build and run the app again, the array filenames will contain names of all the newly added files (good) but also contains names of all the files that were supposed to have been deleted (bad)!!
Can anyone help me understand why I'm seeing this behavior?

Did you do a "Clean" in XCode after adding/removing new items from the bundle? This usually solves the problem of "stale" resources.

Related

How do I add files to the resources folder in XCode?

I want to add a sqlite database to XCode 4 (applies to XCode 3 too). Tutorials state adding the .db file to the resources folder, and I suppose this gets copied to ~/Library/Application Support/iPhone Simulator/4.2/Applications/{some-id}/Documents/ during build where you can find the file with NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)
etc.
However, XCode 4 doesn't have a visible resources folder.
I've tried adding a file with the Add File... command, and then it appears in Targets > AppName > Copy Bundle Resources, but always an empty .db file appears in the above documents folder (which I then manually replace - obviously not the correct approach!)
(due to the nature of the data I'm sticking with sqlite over CoreData)
you have to start adding your db in your project-xcode, so it will be added in your bundle folder, where you can find it via code:
[NSBundle mainBundle]
It's the only folder where you can add files via xcode when you "build" your app (eventually with subfolders, but not "system" folders as "documents") now you just need to keep in mind that the main bundle folder is just "read only", so you cant use your db there with write privileges.
So the normal way is:
1) when you wanna use your db, check via code if it exists in the app:documents folder.
Of course the first time it doesn't, so
2) copy it from the main bundle
- (BOOL)transferDb {
NSError **error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"yourData.db"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath: path])
{
NSString *bundle = [[ NSBundle mainBundle] pathForResource:#"preferenze" ofType:#"plist"];
[fileManager copyItemAtPath:bundle toPath:path error:error];
return YES;
}
return NO;
}
3) use it (r/w) in the documents folder
ps:
and (of course) keep in mind that when you use/edit/write the db in iPhone/simulator, maybe adding records, the one in the main bundle and of course the one in your mac/project won't be updated, no records will be added to it, so if for any reason you delete your app on iPhone/simulator (or "clean all targets" by the xCode "build" menu) the check/copy method will copy the "virgin" db in the documents folder again, so you will loose all your changes...
Be careful with the amount of data you are putting into the Documents folder, this is meant for user data and since this data will be backed up using iCloud Apple have limited the amount of data an app can store and use in the Documents folder.
My app was rejected for using a 6MB SQLite database in this way. Instead copy it to the caches directory: NSCachesDirectory.
Or prevent the file from being backed up: https://developer.apple.com/library/ios/#qa/qa1719/_index.html

iPhone Simulator - Writing to File/Reading problems

I have been reading alot on iPhone read/writing and I believe that everything I have is correct but for some reason it isn't working the way it should.
code is as follows
NSString *docsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [docsDirectory stringByAppendingFormat:#"filename.rtf"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:filePath])
I went into the folder iphone simulator documents folder for the app I am making and added that file into it as a safety measure and it is still not triggering the if statement. So for some reason it is not finding it which is making all of my writing and reading impossible. I even tried using the FileManager's createFile function and didn't get any results. If I need to post more code of something let me know but I figure if all of the above is correct it should be finding the file correctly. Thanks in advance.
UPDATE: Fixed. Hilariously simple mistake. Thanks everyone.
The NSString returned by NSSearchPathForDirectoriesInDomains does not have a trailing slash, thus the path you are building is incorrect. To append the filename to the use stringByAppendingPathComponent which will handle the path separaters correctly.
To create the filePath, try:
NSString *filePath =[docsDirector stringByAppendingPathComponent:#"filename.rtf"];
Have you tried using stringByAppendingPathComponent: instead of stringByAppendingFormat:. If docsDirectory does not contain a trailing slash, you will actually be creating a file called /PATH/TO/Documentsfilename.rtf rather than /PATH/TO/Documents/filename.rtf.

Is possible to read plist from application bundle and documents folder at the same time?

Is it possible?to read from my local bundle and at the same time also read from documents folder into UItableview?
thanks thanks
yes.simultaneously
No — as in the iPhone isn't multicore, you can't have "simultaneous" :p
Yes — as in you can open multiple files in the same period of time. There's no conflicts as long as the files are different (if the files are the same then it depends on how others are using and locking the file etc.)
on viewDidLoad or some similar event when you would be populating your table data, you would simply just aggregate the two files together... that is you are are likely populating an array or dictionary with the contents of the file in question... so use the mutable version of array/dictionary, initialize it empty, then read in the first file from whatever location you choose, populating into your mutable array/dictionary, then do the the same for the next file. after you are done, reloadData as you normally would as if you had read form one file.
As far as simultaneous goes, technically no. However, one could have two different active threads each one reading required files and parsing the data.
Regarding the files you want to access...
Here is a quick and dirty method I use in one project (which I just happen to be working on at the moment):
NSFileManager* fileManager = [NSFileManager defaultManager];
NSError* error;
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
self.documentsDirectory = [paths objectAtIndex:0];
self.blahDBPath = [self.documentsDirectory stringByAppendingPathComponent: #"blah.db"];
NSLog(#"Mainbundle Resourcepath: %#", [[NSBundle mainBundle] resourcePath]);
NSString* defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"blah.db"];
NSLog(#"Default DB Path: %#", defaultDBPath);
success = [fileManager copyItemAtPath:defaultDBPath toPath:self.blahDBPath error:&error];
if (!success) {
NSAssert1(0, #"blah blah blah '%#'.", [error localizedDescription]);
}
It is ugly but effective. I'll rewrite it when I refactor the application.
The point here is that I ask the operating system for the path to certain directories. Then I add file names or subdirectories as required. This allows the operating system to manage paths (like in the simulator where each successive build gets a new unique id as part of its path) and I just worry about the final directories and file names in the application.
One I have the paths, I copy the required file from the bundle directory and put them somewhere, the Documents directory in this case. Then I can do whatever I need to with them.
If I just wanted to access them as they are in the bundle directory, then I I just refer to them by using [[NSBundle mainBundle] resourcePath].
I think something along the lines of the above snippet is what you are looking for.
-isdi-

iphone storing and then reading a file from Documents folder

this must be easy but I want to put a file in the Documents folder which is read in at start up. I have the code on how to read and have confirmed its looking in the correct directory. However the file I have ,RootList.txt when saved in the Resources folder in xcode, is stored under the Root.app folder and the Documents folder is empty. Therefore its not finding this file when I start the app.
Is there a way to ensure a file is built into the Documents directory at start up (I'm running this in the simulator).
The alternative is a plist which works fine as well but I'm just curious.
In these situations I follow this approach:
First save your RootList.txt in Resources folder in xCode.You have nothing in your Documents folder, yet.
In the beginning of your applicationDidLaunch call, do:
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *docsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *path = [docsDirectory stringByAppendingPathComponent:#"RootList.txt"];
if(![fileManager fileExistsAtPath:path])
{
NSData *data = [NSData dataWithContentsOfFile:[[[NSBundle mainBundle] resourcePath] stringByAppendingString:#"/RootList.txt"]];
[data writeToFile:path atomically:YES];
}
Now, your file is in Documents folder at startup.

When installing an App to the iPhone, is there a way to install files to the App's Documents folder?

When installing an App to the iPhone, is there a way to install files to the App's Documents folder?
To say it another way: When the user downloads an App and installs it on their iPhone, I want to automatically install some files to the App's Documents directory.
For example: I have a file foo.txt that I create at development time for my App SeeFooRun. When the App installs, I want foo.txt to appear in the Documents directory so that when I run the App for the first time I access foo.txt from the Documents directory instead of from the App Bundle.
Thanks!
In the example I said "runs for the first time" when I meant to say "installs" and I changed the rest of the sentence to fit. Sorry for the mix up!
I would do it using this method:
Create a property in the root plist set the initial value to "NO" When the app is run, check this value.
If the value is "NO", create the document in the document directory, change the value to "YES" and save the value.
The next time the app is run, the value will be "YES" and the file won't be rebuilt.
Hope this helps.....
You can do something like this:
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *pathLocal = [documentsDir stringByAppendingPathComponent:#"foo.txt"];
NSString *pathBundle = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"foo.txt"];
NSError *error;
BOOL success = [fileManager copyItemAtPath:pathBundle toPath:pathLocal error:&error];
One slight variation on the copying into Documents answers - put everything you copy into a folder in Documents that you create, then at startup you simply check for the presence of that one folder and do all the copying then.
Checking per file is more robust though, especially if you add a new file in an update then the copy logic will copy in the single new file without overwriting the older ones.