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

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

Related

default document directory changes after deleting, re running the application in xcode.

I am not sure if it is the expected behavior.
I have xcode 4.3.2, and running an application, where in i get the default Document directory by the following code.
NSArray *dirPaths;
NSString *docsDirectory;
NSString *databasePath;
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDirectory = [dirPaths objectAtIndex:0];
databasePath = [[NSString alloc] initWithString: [docsDirectory stringByAppendingPathComponent: DBNAME]];
const char *dbpath = [databasePath UTF8String];
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([filemgr fileExistsAtPath:databasePath] == NO) {
NSLog (#" GETDBPATHNAME: database file does not exist");
}
else {
NSLog (#" GETDBPATHNAME: Database File EXISTS");
}
Use Cases:
1) I run the application each time in xcode the path is same.
2) Reboot the device, and re run the application in xcode, also the path is same.
3) BUT WHEN I DELETE THE APPLICATION, AND RE-RUN IN THE PATH DIFFERS. How it is possible?
What get the path something like:
/var/mobile/Applications/xxxxxxx-xxx-xxxx-xxx-xxxxxxxxxxx/Documents/
The value xxxxxxx-xxx-xxxx-xxx-xxxxxxxxxxx claimed to be a Phone UUID. How it is different each time?
Your help is much appreciated.
Each application has a UNIQUE document directory.
When you delete an APP the document directory is also get deleted. Then when you install the same application. The iOS generates another directory for the APP. It never use the previous directory name for this purpose.
Whenever you delete and reinstall the application this path changes.
xxxxxxx-xxx-xxxx-xxx-xxxxxxxxxxx is not the phone UUID otherwise this path would have been the same for every application. Further More this path is also changed when you update your application on the app store.
So the solution to avoid this problem is to save the path after /Documents Directory and use
stringByAppendingPathComponent:
after the path to the Documents Directory to get the full path. In this way you would not have any problem regarding the path of the resource.

Including Core Data in app bundle

I'm not sure that I completely understand how Core Data works on iOS.
I have a large JSON file which I have parsed and imported into core data. This generates an sqlite file. I need this file to be included with the app but every time I delete the app from the device - I have to run the JSON parse script again to create a new sqlite file on the device. I want to be able to exclude the JSON file from the application bundle and dont want to run the parsing script on first use.
How do I go about doing this? Am I doing something wrong?
You will need to create the sqlite file (using your app if you like), then copy it into your project and deploy it with the app. You will also need to add some code to move the file into your documents directory when your app runs for the first time. It can be a simple if file doesn't exist then copy it script.
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *DB = [[paths lastObject] stringByAppendingPathComponent:#"myDB.sqlite"];
if (![fileManager fileExistsAtPath:DB]) {
NSString *shippedDB = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"myDB.sqlite"];
[fileManager copyItemAtPath:shippedDB toPath:DB error:&error];
}
I use this method to ship out pre-built sqlite files, although I haven't used it when CoreData is managing the sqlite file.

How can i change sqlite ReadOnly to ReadWrite on the iPhone?

i deployed my App to my iPhone and get
Unknown error calling sqlite3_step (8: attempt to write a readonly database) eu
on Insert / Update Statements.
On the Simulator it all works like it should.
My sqlite Database is placed in the Resource Folder (Xcode).
Thanks for help!
Your application bundle is not writable on the iPhone. You MUST copy the file somewhere else, like your documents folder. It works in the simulator because the Mac does not enforce all the sandboxing restrictions the iPhone does.
You can copy your database from the application bundle directory to the Documents directory in viewDidLoad. You can read/write from/to your database in the Documents directory after this. Of course, you need to check if the database in the Documents directory exist before you do the copy in order not to overwrite it the next time you bring up the app.
Assuming you have defined your database name '#define kFilename #"yourdatabase.db"' in the .m file.
In viewDidLoad add:
// Get the path to the main bundle resource directory.
NSString *pathsToReources = [[NSBundle mainBundle] resourcePath];
NSString *yourOriginalDatabasePath = [pathsToResources stringByAppendingPathComponent:kFilename];
// Create the path to the database in the Documents directory.
NSArray *pathsToDocuments = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [pathsToDocuments objectAtIndex:0];
NSString *yourNewDatabasePath = [documentsDirectory stringByAppendingPathComponent:kFilename];
if (![[NSFileManager defaultManager] isReadableFileAtPath:yourNewDatabasePath]) {
if ([[NSFileManager defaultManager] copyItemAtPath:yourOriginalDatabasePath toPath:yourNewDatabasePath error:NULL] != YES)
NSAssert2(0, #"Fail to copy database from %# to %#", yourOriginalDatabasePath, yourNewDatabasePath);
}
Good luck!
aobs

how to copy files to Library or Document dir in iphone build phase?

I want copy some files to Library or Document in the Product directory,
but when I add a build phase and select Products Directory in destination and input my subdir it's like Library/xxx/.
When I run the App in the simulator, I found nothing in the destination and if I set the destination to Resources, it will be there.
Can you please tell me why this is happening and how I can make it?
The documents directory should only be accessed at runtime. To my knowledge you cannot copy items there during building. What you should do is store those objects that you want in the Documents directory within your bundle and then copy them upon first launch.
Interesting question. I don't know if you can do this as part of the build phase.
To get something into the Documents directory, I have a method that runs at app startup like this:
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writablePath = [documentsDirectory stringByAppendingPathComponent:#"filename.txt"];
BOOL success = [fileManager copyItemAtPath:defaultDBPath writablePath error:&error];
That's an incomplete fragment but you probably get the idea. The important thing is the NSDocumentDirectory constant.
Add a new build phase -> New Copy Files Build Phase in Target

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.