How can i change sqlite ReadOnly to ReadWrite on the iPhone? - 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

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.

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

How to add existent database into my app in iphone sdk

I have to add an existent DB into my iphone app
I' ve tried to put it into the project's folder and used this init function
-(id) initDatabase{
databaseName = #"mydatabase.sqlite";
// Get the path to the documents directory and append the databaseName
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
[databasePath retain];
return self;
}
and then i used this function
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK){ //inserting methods
but I have an error: the table 'nametable' does not exists, I am sure that this table exists and I think that these methods create a new db file into the device path.
You can't have any files in the documents folder to start. Everything that comes with the app has to be included in the app bundle. The first time the app runs, the documents folder is always empty.
You need to locate the file inside the app bundle using NSBundle's pathForResource:ofType:.
If the DB is readonly, you can just leave it there. If it is readwrite, you need to copy the file into the documents or library folder the first time the app launches.
Update:
could you make an example about using
'NSBundle's pathForResource:ofType:'
please?I'm sorry but I'm new to iphone
sdk
It's just:
NSString *path=[[NSBundle mainBundle] pathForResource:#"myDatabase" ofType:#"sqlite"];
In the sidebar, check "Targets" => (Your Target) => "Copy Bundle Resources". I think your database file might not have been copied because Xcode has no idea what file type that is.

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.

Compiling an XML file into a binary

I want to parse and XML file in an iPhone app without going to disk for it.
Can I do this? How? I have included a TXT helpfile in an app using MSVC.
I put the XML file in a Folder/Group named Resources in the project.
I have the XML file in the proj directory.
I right clicked on Resources folder and selected add -> Existing File.
I right-click on the XML file and select GetInfo.
There I have tried altering the Path Type {Absolute, Relative to Project , etc}
My program runs fine on the simulator when I use:
NSString * const DG_XmlRecipeFile = #"/Users/appleuser/Cocoa/iHungry6/Recipes.xml";
It seems to me it should also work with:
NSString * const DG_XmlRecipeFile = #"Recipes.xml";
If I set the Path Type correctly. It does not.
I am a first timer. Thanks for reading this , Mark
Xcode copies the project resources to the app bundle. You can access your file within your bundle as follows:
NSString *DG_XmlRecipeFile = [[NSBundle mainBundle] pathForResource:#"Recipes" ofType:#".xml"];
Files in the bundle are read-only. If you want to modify the file you will need to copy it somewhere that you can modify it. Your app's Documents directory works well for this.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDirectory = [[paths objectAtIndex:0] retain];
NSString *newFilePath = [docsDirectory stringByAppendingPathComponent:#"Recipes.xml"];
NSError *error = nil;
[[NSFileManager defaultManager] copyItemAtPath:DG_XmlRecipeFile toPath:newFilePath error:&error];
I don't think the path type you are referring to is the path to the resource within the app bundle that is produced. It is how the file should be referenced within the .proj file.