Previously I used the SQLite3 library that comes with Xcode, the code to create a database can be found below:
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = dirPaths[0];
databasePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent:#"databases/database.sqlite"]];
NSLog(#"DB Path: %#", databasePath);
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([filemgr fileExistsAtPath: databasePath ] == NO) {
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &myDatabase) == SQLITE_OK) {
char *errMsg;
const char *sql_data = "CREATE TABLE IF NOT EXISTS myTableName.....";
if (sqlite3_exec(myDatabase, sql_data, NULL, NULL, &errMsg) == SQLITE_OK) {
NSLog(#"Database OK");
[self setDatabase];
} else {
NSLog(#"Data Base Fail");
}
sqlite3_close(myDatabase);
} else {
NSLog(#"Database fail");
}
}
Most recently I had the need to encrypt a database file, and performing a brief search on the internet, I found that some sites recommend using SQL Cipher.
I implemented it in my code in the way that is described in this link, and the only thing I had to do was stay with that code (which I stated earlier), and use these two commands after sqlite3_open:
const char* keyTB = [#"MySecretPassword" UTF8String];
sqlite3_key(myDatabase, keyTB, (int)strlen(keyTB));
To check if my database was encrypted I open it with a text editor and I saw this:
øøèDEÆ?>o›$™;⁄iìÚÄ’†í]¥d=ˇÓä\Êź$4áÓÈ?ïÒ[ÅaÚvÁƒ•i%í≈ª¢.£s◊Âc®Øì≈ àÜU—–}Gec‹≥’B∂¡¸¸Æ™√3Ìnú»YÆ"ß
¬?wÚ÷fñoÂ≈ÛͯzÏâ⁄˛Ct°˘ΩfìÙº0ˇfi]
‚ŸSw∂â≤≥‘=�H€BN±HÇûß…∑º.náaߨO¬ˇ¢(B¨‹óµ¬;º‹ÀÒ
Is it really that SQL Cipher encrypted my database (256-bit AES encryption)? or need to do some configuration in my code?
It is recommended that you not directly embed the password to the database, however that is not a strict requirement for SQLCipher to work. Once you have SQLCipher integrated within your application, you do simply just need to key the database with sqlite3_key once you open the connection. To verify the state of an encrypted database, typically one will run hexdump -C on the database file itself, the content should appear indecipherable.
Related
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 have a UIWebView over a UIScrollView. i use some js files to draw some graph like line that will update when the time value changes.
The Problem
Im not able to scroll when the points goes out of the screen.
I'm new to IOS Development so please help me.
thank in advance
After Completion the QUERY, You need to close transaction.
Here's the sample code for you...
// 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: #"YOURDB.db"]];
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &DB) == SQLITE_OK)
{
NSString *query=[NSString stringWithFormat:#"insert into studentDetails (NAME,Email,adressL1,adressL2,phone,landline,department,DoB,fatherName) values (\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\")",
name.text,
email.text,
addressLine1.text,
addressLine2.text,
phone.text,
Landline.text,
Department.text,
DoB.text,
fname.text];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(YOURDB, insert_stmt, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
NSLog(#" Successfully added");
} else {
NSLog(#" Failed added");
NSLog(#"Error %s",sqlite3_errmsg(ExplorejaipurDB));
}
}
sqlite3_finalize(statement);
sqlite3_close(YOURDB);
}
The database could be locked because of several reasons:
Multiple queries running
multiple threads running
opened the database multiple times
Check your code and see if you have closed the connections to the database sqlite3_close(). A good idea would also be to use sqlite3_finalize() after each SQL statement when you are done with it.
So try try to match all your sqlite3_open() with sqlite3_close() and sqlite3_prepare() (if you are using it) with sqlite3_finalize()
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
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];
}
I used the following code for database creation in Xcode. It runs smoothly up to the NSFilemanager code, but after that it will terminating to else code that says status.text=#"failed to open/create database"; so table can't be created.
I imported sqlite3.h and create sqlDatabase reference variable sqlite3 still it doesn't work.
-(void)databaseCreate
{
NSString *docsDir;
NSString *dbPath;
NSArray *dirPath;
dirPath=NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES);
NSLog(#"dirpath::%#",dirPath);
docsDir=[dirPath objectAtIndex:0];
NSLog(#"document directory::%#",docsDir);
dbPath=[[NSString alloc] initWithString:[docsDir stringByAppendingPathComponent:#"timerpro1.db"]];
NSLog(#"database path::%#",dbPath);
NSFileManager *fileManager=[NSFileManager defaultManager];
if([fileManager fileExistsAtPath: dbPath] == NO)
{
const char *databsPath=[dbPath UTF8String];
NSLog(#"treat filemanager");
if(sqlite3_open(databsPath,&sqlDatabase) == SQLITE_OK)
{
char *err;
NSLog(#"create inside");
const char *sql_stmt="CREATE TABLE IF NOT EXISTS PRJDATA(ID INTEGER PRIMERY KEY AUTOINCREMENT,PRJ_NAME TEXT,PRJ_DATE TEXT,TIME_POINT1 TEXT,TIME_POINT2 TEXT,TIME_POINT3 TEXT,POINT2_DIFF_MIN TEXT,POINT2_DIFF_SEC TEXT,POINT3_DIFF_MIN TEXT,POINT3_DIFF_SEC TEXT)";
if (sqlite3_exec(sqlDatabase, sql_stmt, NULL, NULL, &err)!=SQLITE_OK)
{
status.text=#"failed to create table";
}
sqlite3_close(sqlDatabase);
}
else
{
status.text=#"failed to open/create database";
}
}
[fileManager release];
}
The immediate problem is that you have to replace the reference to NSDocumentationDirectory with NSDocumentDirectory.
Two asides:
When you get failures, you should examine sqlite3_errmsg(), as you'll often get descriptive errors. For example, your sqlite3_exec() statement will fail, even after you fix the NSDocumentDirectory mistake.
If you look at the sqlite3_errmsg() error message, it will tell you that you have an error near the AUTOINCREMENT clause. If you look at the SQL carefully, you'll notice that you have misspelled PRIMARY KEY. That would be more difficult to find in the absence of the sqlite3_errmsg(), which brings our attention to the particular portion of the SQL.
You can simplify dbPath declaration:
dbPath=[docsDir stringByAppendingPathComponent:#"timerpro1.db"];`