I am updating an entry in a database, the statement works and the code is updated in the database. Once the app has been closed and reopened though the database has not saved. It seems to create a temporary database and then not actually save to the database that the app is reading from.
Here is my code:
-(void)updateDatabase:(int)Level andPlayer:(int)questionID{
DataClass *obj=[DataClass getInstance];
//NSFileManager *filemgr = [NSFileManager defaultManager];
NSString* Database =[NSString stringWithFormat:#"levelProgress.db"];
NSString* databaseP = [[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:Database];
databasePath = [[NSString alloc]initWithString:databaseP];
const char *dbpath = [databasePath UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &questionDB) == SQLITE_OK) {
NSString *querySQL = [NSString stringWithFormat:#"UPDATE levelProgress SET completed_questions=completed_questions+1 WHERE level=%d", obj.levelSelected];
const char *query_stmt = [querySQL UTF8String];
sqlite3_prepare_v2(questionDB, query_stmt, -1, &statement, NULL);
if(sqlite3_step(statement)==SQLITE_DONE){
NSLog(#"update worked");
}else{
NSLog(#"did not work");
}
sqlite3_finalize(statement);
sqlite3_close(questionDB);
}
}
Any help with this would be greatly appreciated.
Copy database file databaseP from application bundle into user folder and then update that. You can't update any file in application bundle (they are always read only).
#define kDatabaseName (#"levelProgress.db")
- (void)checkAndCopyDatabaseIfNeeded
{
if (!self.databasePath)
{
// Database should be present in user sandbox at root.
self.databasePath = [NSString pathWithComponents:[NSArray arrayWithObjects:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject], kDatabaseName, nil]];
}
// Check if the file already copied/exists.
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL success = [fileManager fileExistsAtPath:self.databasePath];
if(!success)
{
// Copy the file from app bundle to user sandbox (Files in app bundle can not be edited).
NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:kDatabaseName];
#if DEBUG
BOOL isCopied =
#endif
[fileManager copyItemAtPath:databasePathFromApp toPath:self.databasePath error:nil];
NSAssert(isCopied, #"Problem copying database file to user document folder");
}
}
When the database is in the bundle it is read only. You will need to have your database in your documents directory when writing to it!
Have a look at the accepted answer here: Use and Access Existing SQLite Database on iOS
Related
Ok so I have a database in my iPhone simulator documents. And I now know for sure it's in the applications sandbox. Something is funky in the code I have. So I first get the DB path:
-(NSString *)getsynDbPath
{
NSString* dataBAse = [[NSBundle mainBundle] pathForResource:#"ddd"ofType:#"sqlite"];
return dataBAse;
}
Then I test the path:
NSString *testData;
testData = [self getsynDbPath];
NSFileManager * fileManager = [NSFileManager defaultManager];
BOOL success = [fileManager fileExistsAtPath:testData];
if (success) {
NSLog(#"Oh no! There was a big problem!");
} else {
//Successfully opened
if(sqlite3_open([testData UTF8String], &db)==SQLITE_OK){
NSLog(#"Raise the roof!");
//Calling method to loop through columns
[self listOfCols];
}
}
I then go to a custom method where I loop through the columns inside the database:
-(NSArray *)listOfCols{
NSMutableArray *retval = [[[NSMutableArray alloc]init]autorelease];
NSString *query = #"SELECT KEY_ID FROM CON_DETAIL";
sqlite3_stmt *statement;
//Does not execute
if (sqlite3_prepare_v2(db, [query UTF8String], -1, &statement, nil)==SQLITE_OK) {
while (sqlite3_step(statement)==SQLITE_ROW) {
int key_id = sqlite3_column_int(statement, 0);
NSLog(#"Key ID: %d", key_id);
char *nameChars = (char *) sqlite3_column_text(statement, 1);
NSLog(#"chars %s", nameChars);
char *cityChars = (char *) sqlite3_column_text(statement, 2);
NSLog(#"chars %s", cityChars);
}
}
NSLog(#"%s Why '%s' (%1d)", __FUNCTION__, sqlite3_errmsg(db), sqlite3_errcode(db));
return retval;
}
So here's my question. After I successfully opened the database, why the heck am I getting a log error that says: no such table: CON_DETAIL ? Any help is appreciated.
I think you have to copy your db in your document directory and then try to fetch. Copy it with following functions.
-(void) dbconnect{
self.databaseName = #”yourdbname.sqlite”;
// Get the path to the documents directory and append the databaseName
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
self.databasePath = [documentsDir stringByAppendingPathComponent:self.databaseName];
// Execute the “checkAndCreateDatabase” function
[self checkAndCreateDatabase];
}
-(void) checkAndCreateDatabase{
// Check if the SQL database has already been saved to the users phone, if not then copy it over
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:self.databasePath error:nil];
[fileManager release];
}
NOTE: If you are not getting db in your app’s document directory do the following.
Go to : Target -> “Build Phases” -> “copy bundle Resources” Then add that particular file here.
After that call your "listOfCols" method.
I am using sqlite3 in my project.
I am getting error after couple(50-60) of transaction that "unable to open
database file",So check my database file path but path is correct and
file is there.
I tried each and every solution discussed on stack overflow, but with no
luck.
I check my "DocumentDirectory" path, done all necessary step before to close database. Like:
sqlite3_finalize(selectStatement);
sqlite3_close(database);
I don't know how to tackle this problem.can I check that my sqlite3 database is open or not.
====================== 1============================
+(NSMutableArray*)executeDataSet:(NSString*)query
{
NSMutableArray *arryResult = [[NSMutableArray alloc] initWithCapacity:0];
const char *sql = [query UTF8String];
sqlite3_stmt *selectStatement;
sqlite3 *database = [DataBaseClass openDataBase];
//prepare the select statement
int returnValue = sqlite3_prepare_v2(database, sql, -1, &selectStatement, NULL);
if(returnValue == SQLITE_OK)
{
//my code
}
}
//sqlite3_reset(selectStatement);
// NILOBJECT(selectStatement);
// NILOBJECT(selectStatement);
sqlite3_finalize(selectStatement);
sqlite3_close(database);
return arryResult;
}
==================== 2 =================================
+(sqlite3 *) openDataBase {
sqlite3 * edenAdultDatabase;
NSString * databasePath =[DataBaseClass pathForDatabase];
if(sqlite3_open([databasePath UTF8String], &edenAdultDatabase) == SQLITE_OK) {
NSLog(#"Yes database is open");
return edenAdultDatabase;
}
else
{
NSLog(#"do something %s",sqlite3_errmsg(edenAdultDatabase));
}
return edenAdultDatabase;
}
====================== 3 ===========================
+(NSString *) pathForDatabase {
NSString *libraryDir = [FileManager pathForPrivateDocumentsFolder];
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSError *error;
NSString *privateFolderPath = [libraryDir stringByAppendingPathComponent:#"DataBase"];
if (![fileMgr fileExistsAtPath:privateFolderPath])
{
[fileMgr createDirectoryAtPath:privateFolderPath withIntermediateDirectories:NO attributes:nil error:&error];
}
/*
// My database in library private folder ..this is just for test.
// I copied databae to document dir but no luck.
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
privateFolderPath = [documentsDir stringByAppendingPathComponent:kDatabaseName];
*/
privateFolderPath = [privateFolderPath stringByAppendingPathComponent:kDatabaseName];
return privateFolderPath;
}
There is a weird issue in database connectivity, sometime it does not connect. Therefore it is recommended by people that your application should open a database once (during in initialisation phase) and close the connection when application is terminating.
Reference: Sqlite opening issue
Regarding checking database connectivity, Sqlite3 does not provide any method to check that either database is open or not.
By using Shared instance of database manager, you can achieve it. Define a boolean at class level and set it's value when you open the database:
// .h file
BOOL isDatabaseOpen;
// .m file
-(void) openDatabase
{
if(![self isDatabaseExist])
{
// copy database to library
[self copyDatabaseFile];
}
NSString *sqLiteDb = [self getDatabaseLibraryPath];
if (sqlite3_open([sqLiteDb UTF8String], &_databaseHandler) != SQLITE_OK) {
NSLog(#"Database --> Failed to open");
isDatabaseOpen = NO;
}
else
{
isDatabaseOpen = YES;
}
}
and then you can use the following method to check is database opened or not.
-(BOOL) isDatabaseOpen
{
return isDatabaseOpen;
}
Let me know if it worked :).
check out this kind of solution
first of all create function like below:
-(void)checkDBAndCopy{
NSArray *dirPath=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *connectionPath=[dirPath objectAtIndex:0];
strDBPath=[connectionPath stringByAppendingPathComponent:#"database.sqlite"];
NSLog(#"%#",strDBPath);
NSFileManager *filemanager=[[NSFileManager alloc]init];
if (![filemanager fileExistsAtPath:strDBPath]) {
NSString *databasePathFromApp=[[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:#"database.sqlite"];
[filemanager copyItemAtPath:databasePathFromApp toPath:strDBPath error:nil];
}
}
and call this function like below method:-
-(NSMutableArray *)RetriveSharedspots:(NSString *)Query{
[self checkDBAndCopy];
if (sqlite3_open([strDBPath UTF8String], &contactDB)==SQLITE_OK) {
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(contactDB, [Query UTF8String],-1,&statement,NULL)==SQLITE_OK)
{
while (sqlite3_step(statement)==SQLITE_ROW) {
// Your code
}
}
sqlite3_finalize(statement);
}
sqlite3_close(databaseName);
return array;
}
Above this worked for me great. try this.
Just sharing my case with this issue.
I have a project that uses databaseFile1.sqlite and I am not sure if there was a build of it installed on my simulator.
Then I changed the database file, say databaseFile2.sqlite different contents, different filename. Then this issue came up. As I read the solutions and comments, I realized that the issue shouldn't be so biggie.
Welp, I deleted the build and restarted Xcode. Voila. It's okay now. Later on, I will revert back to databaseFile1.sqlite the database, and I'll see if this issue can be reproduced.
i want to secure my sqlite database store in doc directory. so i used this for encryption.
[fileManager createFileAtPath:databasePath contents:[#"super secret file contents" dataUsingEncoding:NSUTF8StringEncoding] attributes:[NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSFileProtectionKey]];
My code is like this
-(void)createDatabase
{
// Setup some globals
NSString *databaseName = #"Sample.sqlite";
// Get the path to the documents directory and append the databaseName
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
NSString *databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
// Check if the SQL database has already been saved to the users phone, if not then copy it over
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 createFileAtPath:databasePath contents:[#"super secret file contents" dataUsingEncoding:NSUTF8StringEncoding] attributes:[NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSFileProtectionKey]];
}
-(void)opendb
{
sqlite3 * database;
NSString *databasename=#"Sample.sqlite"; // Your database Name.
NSArray * documentpath=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSAllDomainsMask, YES);
NSString * DocDir=[documentpath objectAtIndex:0];
NSString * databasepath=[DocDir stringByAppendingPathComponent:databasename];
if(sqlite3_open([databasepath UTF8String], &database) == SQLITE_OK)
{
const char *sqlStatement = "SELECT * FROM Sampletable"; // Your Tablename
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK)
{
while(sqlite3_step(compiledStatement) == SQLITE_ROW)
{
[Rollnumber addObject:[NSNumber numberWithInt:(int)sqlite3_column_text(compiledStatement,0)]];
[Name addObject:[NSString stringWithFormat:#"%s",(char *) sqlite3_column_text(compiledStatement, 1)]];
}
}
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
Roll number and name not being access.
but when i removed this line
[fileManager createFileAtPath:databasePath contents:[#"super secret file contents" dataUsingEncoding:NSUTF8StringEncoding] attributes:[NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSFileProtectionKey]];
once again properly work but i want to encrypt my database using this code so how may i aceess my sqlite content. means how may i decrypt when i want to use.
please help me to do this.
Thanks in advance.
my db encrypt successfully. but when i want to access the sqlite. not get the data.
so how may i do this thanks in advance.
You should consider using SQLCipher, that should completely do what you want. SQLCipher was already discussed here: Encrypting SQLite database file on iOS
If you want to roll something yourself, you will have to read the full database into memory, use some decryption code of your now and save the encrypted version as you try above. This nevertheless isn't too easy to do if you have to stay within memory limits for example. Also, to access the database you have to encrypt it and probably store temporarily on disk. This makes it accessible again.
I'm creating an app for my school project that has to write data to my sqlite database. It works, as long as the app is running active but as soon as the app closes, my added data is gone and when I want to read this data this will not work off course. I included both my loadData and saveData methods. The two database paths are the same in both functions so it's not that I'm writing my data elsewhere. I really can't find the solution or the problem. I even get the insert success in my output, so the insert is successful.
- (void) saveData:(id)sender{
NSString *sqldb = [[NSBundle mainBundle] pathForResource:#"PXLate" ofType:#"sqlite3"];
sqlite3_stmt *stmt;
NSString *queryInsert = #"INSERT INTO assignments (name, lesson, dueDate, notification, start, at) VALUES ('abc','abc', 'abc', 1, 'abc', 'abc')";
NSLog(#"%#",sqldb);
NSLog(#"%#",queryInsert);
if(sqlite3_open([sqldb UTF8String], &_PXLate) == SQLITE_OK)
{
sqlite3_prepare_v2(_PXLate, [queryInsert UTF8String], -1, &stmt, NULL);
if(sqlite3_step(stmt)==SQLITE_DONE)
{
NSLog(#"insert success");
}
else
{
NSLog(#"insert un success");
NSAssert1(0, #"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(_PXLate));
}
int success=sqlite3_step(stmt);
if (success == SQLITE_ERROR)
{
NSAssert1(0, #"Error: failed to insert into the database with message '%s'.", sqlite3_errmsg(_PXLate));
//[_PXLate save:&error];
} sqlite3_finalize(stmt);
}
sqlite3_close(_PXLate);
}
and my loadData function
- (void) loadData:(id)sender
{
//path for database
NSString *sqldb = [[NSBundle mainBundle] pathForResource:#"PXLate" ofType:#"sqlite3"];
//check if present
NSFileManager*fm=[NSFileManager defaultManager];
NSLog(#"path: %#", sqldb);
const char *dbpath = [sqldb UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &_PXLate) == SQLITE_OK)
{
NSString *querySQL = [NSString stringWithFormat: #"SELECT * FROM assignments WHERE name='abc'", _label.text];
const char *query_stmt = [querySQL UTF8String];
NSLog(#"name");
NSLog(querySQL);
int response = sqlite3_prepare_v2(_PXLate, query_stmt, -1, &statement, NULL);
NSLog(#"response %d", response);
if (response == SQLITE_OK)
{
NSLog(#"name");
if (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *namefield = [[NSString alloc]
initWithUTF8String:
(const char *) sqlite3_column_text(
statement, 0)];
NSLog(#"name:%#", namefield);
_label.text = namefield;
} else {
_label.text = #"Match not found";
}
sqlite3_finalize(statement);
}
sqlite3_close(_PXLate);
}
}
You have to copy your sqlite to the documents directory and then work with that. Example:
self.databaseName = #"databasename.sqlite";
// Get the path to the documents directory and append the databaseName
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
self.databasePath = [[NSString alloc]init];
self.databasePath = [documentsDir stringByAppendingPathComponent:self.databaseName];
[self checkAndCreateDatabase];
And the create method:
-(void)checkAndCreateDatabase
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:self.databaseName];
[fileManager copyItemAtPath:databasePathFromApp toPath:self.databasePath error:nil];
}
A couple of observations:
As Retterdesdialogs said, you should
Check for existence of database in Documents;
If not there, copy from bundle to Documents; and
Open database from Documents.
You should not open database from bundle, because on the device that folder is read-only.
In your INSERT statement you are not checking the response of sqlite3_prepare_v2, which is a very common source of errors. If this is not SQLITE_OK, you should immediately log sqlite3_errmsg, before you call sqlite3_step.
You are performing sqlite3_step twice in the INSERT statement.
In loadData, you are not logging sqlite3_errmsg if sqlite3_prepare_v2 failed. Always look at sqlite3_errmsg upon any error.
I am trying to execute a simple hard-coded insert statement for a SqLite database. The code works and I get a success message from my own NSLog, however, no records are added to the database. Can anyone help? THx! Viv
-(void)addFavorites{
const char *sqlInsert = "insert into rivers (stat_ID, stat_Name, state) values ('03186500','WILLIAMS RIVER','WA')";
sqlite3_stmt *statement;
sqlite3_prepare_v2(_database, sqlInsert, -1, &statement, NULL);
if(sqlite3_step(statement) == SQLITE_DONE){
NSLog(#"RECORD ADDED!");
} else {
NSLog(#"RECORD NOT ADDED!");
}
sqlite3_finalize(statement);
}
Do you have code like this in your app delegate to copy the database out of the bundle to your NSDocuments directory? Be sure to copy the database to there, then point to there when you're running your sqlite3_open, not to the bundle. The NSDocument directory will be saved when the device is synced to iTunes or iCloud, so it's the place you want your database to be for maintaining data.
NSString *databaseName = #"MyDatabase.sqlite";
NSArray *systemPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *libraryDirectory = [systemPaths objectAtIndex:0];
NSString *databaseFullPath = [libraryDirectory stringByAppendingFormat:#"%#%#",#"/",databaseName];
//copy the database to the file system if it hasn't been done yet.
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL exists = [fileManager fileExistsAtPath:databaseFullPath];
if(exists == NO)
{
NSString *dbPathInBundle = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:databaseName];
[fileManager copyItemAtPath:dbPathInBundle toPath:databaseFullPath error:nil];
}