Xcode Sqlite persistence problem - iphone

I have a persistence problem in my application. I'm using sqlite database. when some insert queries executed results temporary added to the database. After restarting application the new values vanish! I think new values stored on RAM do not save on hard-disk.
-(IBAction)add:(id)sender
{
NSString *myDB;
NSString *query;
myDB=[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"Ssozluk.sql"];
database =[[Sqlite alloc] init];
[database open:myDB];
query=[NSString stringWithFormat:#"INSERT INTO words(col_1,col_2) VALUES('asd','asd2');"];
[database executeNonQuery:query];
[database commit];
[database close];
}
-(IBAction)show:(id)sender
{
NSString *myDB;
NSString *query;
NSArray *asdasd;
NSString *asd;
myDB=[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"Ssozluk.sql"];
database =[[Sqlite alloc] init];
[database open:myDB];
query=[NSString stringWithFormat:#"Select col_2,col_1 FROM words"];
asdasd=[database executeQuery:query];
for(NSDictionary *row in kelimeler)
{
asd=[row valueForKey:#"col_2"];
olabel1.text=asd;
}
[database commit];
[database close];
}

You need programmatically copy your database to Documents dir of application, and work with writable copy. Resources in bundle is readonly.

I use the following code to copy my database to the documents folder and than get the writable part:
- (void)createEditableCopyOfDatabaseIfNeeded {
// First, test for existence.
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"db.sqlite"];
//NSLog(writableDBPath);
success = [fileManager fileExistsAtPath:writableDBPath];
if (success) return;
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"db.sqlite"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
- (void)initializeDatabase {
// The database is stored in the application bundle.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"db.sqlite"];
// Open the database. The database was prepared outside the application.
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK) {
//Add initial sql statements here
} else {
// Even though the open failed, call close to properly clean up resources.
sqlite3_close(database);
NSAssert1(0, #"Failed to open database with message '%s'.", sqlite3_errmsg(database));
// Additional error handling, as appropriate...
}
}
- (sqlite3*) getDB{
return database;
}
Insert the calls to your applicationDidFinishLaunching method

As far as I remember, when you use path to db the way you do, it should be added to project.

Sometimes, and especially with databases, just cleaning doesn't work!!!
Go to the dir: /Users/qjsi/Library/Application Support/iPhone Simulator/4.3/Applications
There, you'll find all the projects you've run. Find the folder containing the project your working on, and delete it. Then clean your project through Xcode, then run. The folder in that dir will be recreated, and so will the database.
NOTE: The database will be removed as well! If you have it saved in your bundle and copy it to an editable directory, please note that the database will be the same as the one in your bundle (so, without altered records made in the iPhone Simulator).

Related

Can we give hardcoded path for sqlite in Xcode?

I'm new to the iPhone development and Mac OS, please bear with the silly query. But I tried hard to get into depth but couldn't found the solution for the problem.
I have created a database in sqlite through command prompt. The database is saved in Users/DNamto/resources.db
But when am trying to open this db in my iPhone application using the following code snippet
// Get the documents directory
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
// Build the path to the database file
databasePath = [[NSString alloc]initWithString: [docsDir stringByAppendingPathComponent:#"resources.db"]];
The database fails to open up.
The database path which the application is searching for is :
/Users/DNamto/Library/Application Support/iPhone Simulator/6.0/Applications/C82C3DAF-4E95-49A7-9A4F-4D69B056DC9D/Documents/resources.db
Can anyone help me to get the correct database path.
Can we hard code the DB path so that my application links to it. If yes then please provide the code snippet.
Add your database in the application & check if the db is present in doc directory or not, if not then you need to copy it in doc directory and then access it.
For cppy the db in doc directory use following code snippet
- (void)copyDatabaseIfNeeded {
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
success = [fileManager fileExistsAtPath:[self getDBPath]];
NSString *databasePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"dbname.sqlite"];
if(success)
{
return;// remove old one.
}
[fileManager copyItemAtPath:databasePath toPath:[self getDBPath] error:nil];
}
To open the db use following code snippet
-(void)openDatabase
{
#try
{
[self copyDatabaseIfNeeded];
if(sqlite3_open([[self getDBPath] UTF8String], &mainDatabase)==SQLITE_OK)
{
NSLog(#"Database opened");
}
}
#catch (NSException *exception)
{
NSLog(#"Exception in openDatabase %# :%#",exception.name,exception.reason);
}
}
- (NSString *)getDBPath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
return [documentsDir stringByAppendingPathComponent:#"dbname.sqlite"];
}
Use following code snippet to close the database.
-(void)closeDatabase:(sqlite3_stmt*)statement
{
#try
{
sqlite3_finalize(statement);
sqlite3_close(mainDatabase);
}
#catch (NSException *exception)
{
NSLog(#"Exception in DatabaseController closeDatabase %# :%#",exception.name,exception.reason);
}
}
You can't. In real device you can't get the hard coded path.
You need a relative path.
Here your issue is your database is not present in the document directory.
You need to add your database to your main bundle and in run time you need to check whether the db is present on document directory, if not you need to copy it to document directory using the NSFileManager.
You can use the following code to copy the database file from bundle to document directory.
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *sourcePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"resource.db"];
NSString *folderPath = [documentsDirectory stringByAppendingPathComponent:#"resource.db"];
NSError *error;
[[NSFileManager defaultManager] copyItemAtPath:sourcePath toPath:folderPath error:&error];

copying from main bundle creates a file size zero kb

As part of my app start-up i copy bundle files to my documents directory.
This works fine for three out of four of my files but the fourth one create a Zero KB file.
running on iOS 5.0 sim. I have cleaned the build several times and checked the file name capitalization vis correct.
the file appears in the directory but is zero kb and should be 24K
any help appreciated.
-(BOOL) CheckDBs: (NSString *)dbname
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *dbPath = [documentsDir stringByAppendingPathComponent:dbname];
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL success = [fileManager fileExistsAtPath: dbPath];
NSLog(#"AppDelegate CheckDatabase: %# = %i", dbPath, success);
if (success) {
//NSLog(#"return YES");
return YES;
}
else {
return NO;
}
} // Complete - checks if files exist in the User Documents directory
-(void) copyDBs: (NSString *) dbname
{
//Using NSFileManager we can perform many file system operations.
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *dbPath = [documentsDir stringByAppendingPathComponent:dbname];
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:dbname];
BOOL success = [fileManager copyItemAtPath:defaultDBPath toPath:dbPath error:&error];
if (success) {
// Version 4.0 code
//NSDictionary *attribs = [NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSFileProtectionKey];
//success = [fileManager setAttributes:attribs ofItemAtPath:dbPath error:&error];
NSLog(#"AppDelegate copyDatase: %# = %d", dbPath, success);
}
//NSLog(#"AppDelegate copyDatase: %# = %d", dbPath, success);
if (!success) {
NSLog(#"Failed to copy database: '%#'", [error localizedDescription]);
// NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
Have you also checked the original file size?
Try resetting your simulator. From the NSFileManager documentation:
If a file with the same name already exists at dstPath, this method
aborts the copy attempt and returns an appropriate error.
Make sure the destination is empty and try again. Also, check the error object.
If all that checks out there has got to be an error in spelling the file name. Check if the exact file exists in bundle, NSLog wherever you use a file name or path, etc. You should find the error. Also check the appropriate folder in the Finder.
Instead of using
[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:dbname]
try
[[NSBundle mainBundle] pathForResource:shortName ofType:#"db"]
Ok I figured out what is causing the problem.
as i run the app the appdidfinishlaunching method is not complete before one of the view controllers is loading. That view controller attempts to access one of the files being copied over from the bundle.
I'm guessing that sqlite creates the file when you attempt to access the database, it creates it with with a zero bytes length.
So when my appdidfinish launching method checks for the existance of the file it exists due to the sql call.
This is usually only going to be a problem prior to the first run of the app as after that the database will exist.
problem now is how do i get the appdidfinish launching to complete prior to the rest being allow to start as the view controller in question is part of the mainwindow.xib

refresh SQLITE3 (Core Data) on device and app store

I have an app leveraging Core Data SQLITE3 that works perfectly in the simulator. However i do not understand how to update the DB on the device, which i guess is the same as in app-store.
I update the DB from .txt files in the app and create the DB, this function is there only for creating the DB and will be removed in the final version. My idea is to create the DB in the simulator, lock the update part of the code and then distribute the package with an updated database.
However, when i rebuild my app on the device it still have the old data in the DB.
I have been looking around but i am afraid i do not fully understand how to solve this. I did find this thread: Can't refresh iphone sqlite3 database
I would very much appreciate if some nice person could share some light on this and help me to solve this.
Cheers
Have you copied the db file from the bundle directory (which is read only) to a writable one? (like the documents directory of each application?).
When trying to save in the device did you get a sqlite error like this?
SQLITE_READONLY 8 /* Attempt to write a readonly database */
EDIT:
All the files in the main bundle are read only, so if you need to modify one/some of them, you need to copy the files in a location that is writable. Assuming you have called the db mydb.sqlite here is some code that copies the db (only if it does not exists) to the documents directory.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDirectory = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
NSFileManager *fm = [NSFileManager defaultManager];
NSString *docPath = [docDirectory stringByAppendingPathComponent:#"mydb.sqlite"];
if (![fm fileExistsAtPath:docPath]) { // file does not exists, copy it
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:#"mydb" ofType:#"sqlite"];
NSError *error = nil;
BOOL res = [fm copyItemAtPath:bundlePath toPath:docPath error:&error];
if (!res) {
// do something with error
}
}
Actually to use .db file inside the Bundle - it's a very bad idea.
Every thime, when I am using .db file, i am checking, if it allready exists inside my Application document directory, and then I will rewrite it.
#define DB_SHOULD_BE_REWRITTEN YES //You should update database and change allready existing db file to file from bundle
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"db.sqlite"];
BOOL success = [fileManager fileExistsAtPath:writableDBPath];
if (!success || DB_SHOULD_BE_REWRITTEN)
{
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"db.sqlite"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
dbPath = writableDBPath;

SQLite errors using iPhone simulator/Possible permission issue

I'm working on a new iPhone project and I'm running into problems with sqlite. I've done this before on a different project and it worked just fine so I'm not sure exactly what is going on with this one. I'm using the same code from before, but the situation is a little different.
First of all, I'm trying this time to use Unit Testing so I've created a Cocoa Unit Test Bundle, and I got that working correctly, then I wanted to make a Unit Test for my sqlite database.
The first thing run with this test is [self checkAndCreateDatabase] which is as follows:
-(void) checkAndCreateDatabase{
BOOL success;
// Create a FileManager object, we will use this to check the status
// of the database and to copy it over if required
NSFileManager *fileManager = [NSFileManager defaultManager];
// Check if the database has already been created in the users filesystem
success = [fileManager fileExistsAtPath:databasePath];
// If the database already exists then return without doing anything
if(success) return;
// If not then proceed to copy the database from the application to the users filesystem
// Get the path to the database in the application package
NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:databaseName];
// Copy the database from the package to the users filesystem
[fileManager copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
[fileManager release];
}
Then I attempt to open the database with the following line:
int result = sqlite3_open([databasePath UTF8String], &database);
This fails everytime with error code 14 SQLITE_CANTOPEN, and databasePath is "/Users/labuser/Library/Application Support/iPhone Simulator/Documents/projectfusion.db3".
What is odd is that when I go to that directory, Documents/ isn't there, so if I create that, then it doesn't fail, BUT projectfusion.db3 then has a size of 0kb; the tables aren't there. That makes any sqlite3_prepare_v2() fail, because the tables aren't there. If I manually copy the projectfusion.db3 file to that directory before running, then it works just fine.
Is it because I'm doing this inside the unit tests and the scripts don't have permission or something? Or is it possibly because I'm working on a school computer at my university and can't write to that directory? (I tried logging in as admin and it didn't work either).
Try the code given below,
I've set a macro called TEST so I don't have to keep commenting code out.
- (NSString *)createEditableCopyOfDatabaseIfNeeded {
// First, test for existence.
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
#ifdef TEST
documentsDirectory = [[NSFileManager defaultManager] currentDirectoryPath];
#endif
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:DBNAME];
success = [fileManager fileExistsAtPath:writableDBPath];
if (success) { return writableDBPath;} ;
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:DBNAME];
#ifdef TEST
defaultDBPath = [[NSBundle bundleForClass:[self class]] pathForResource:#"YOURDB" ofType:#"sqlite"];
#endif
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
return (defaultDBPath);
}
Try adding the database to your application bundle in xcode and then copy it to the applications Documents directory. You can get that path using
- (NSString *)applicationDocumentsDirectory {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
return basePath;
}

Statements are not getting committed in sqlite3 when using the wrapper

[sqlite executeQuery:#"UPDATE UserAccess SET Answer ='Positano';"];
NSArray *query2 = [sqlite executeQuery:#"SELECT Answer FROM UserAccess;"];
NSDictionary *dict = [query2 objectAtIndex:0];
NSString *itemValue = [dict objectForKey:#"Answer"];
NSLog(#"%#",itemValue);
It does print Positano at this point ..
But when I just print without the update query again . I get the old entry which is Paris.
I tried [sqlite commit];
which also doesn't work.
Even if I create a simple table it executes the first time then again when I hit the create button it says that the table already exists BUT when I rerun again it creates the table again instead of giving me an error that the table already exists .
What am I doing wrong ???
I am using http://th30z.netsons.org/2008/11/objective-c-sqlite-wrapper/
wrapper.
Regards ,
Novice
It looks like you didn't commit the transaction.
In the file Sqlite.m, there are methods beginTransaction and commit. Or you may try to set the db to the autocommit mode.
-(void)createEditableCopyOfDatabaseIfNeeded
{
// Testing for existence
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"MyDatabase.sqlite"];
success = [fileManager fileExistsAtPath:writableDBPath];
if (success) return;
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"MyDatabase.sqlite"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if(!success)
{
NSAssert1(0,#"Failed to create writable database file with Message : '%#'.",[error localizedDescription]);
}
}
In xxxxAppdelegate.m
That was the solution