SQLite query runs on SQLiteManager but not on iOS - ios

Having an sqlite issue. The query runs fine on the firefox addon SQLiteManager. It does not run on iOS however. I get an error code of 1. The database gets connected to fine.
NSString *sqLiteDb = [[NSBundle mainBundle] pathForResource:#"featureDB"
ofType:#"sqlite3"];
if (sqlite3_open([sqLiteDb UTF8String], &_database) != SQLITE_OK) {
DLog(#"Failed to open database!");
} else {
DLog(#"Connected to db");
}
NSString *query = [NSString stringWithFormat:#"SELECT * FROM featureTable"];
sqlite3_stmt *statement;
NSLog(#"could not prepare statement: %s\n", sqlite3_errmsg(_database));
if (sqlite3_prepare_v2(_database, [query UTF8String], -1, &statement, nil) == SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW) {
DLog(#"row");
int uniqueId = sqlite3_column_int(statement, 0);
}
sqlite3_finalize(statement);
DLog(#"sqliteStep");
} else {
DLog(#"statement Error %d", sqlite3_prepare_v2(_database, [query UTF8String], -1, &statement, nil));
}
The log for "row or sqliteStep" never fires.

Your else statement should not call sqlite3_prepare_v2 again, but rather should:
DLog(#"prepare error: %s", sqlite3_errmsg(_database));
What does this error message report?
Note: You already are logging sqlite3_errmsg before you call sqlite3_prepare_v2. Of course that's going to report no error, because no error could have possibly has taken place yet. Do this logging inside the else clause where you know sqlite3_prepare_v2 (a) has been called; but (b) didn't return SQLITE_OK. And don't call any SQLite functions between when sqlite3_prepare_v2 failed and where you log the error message.
The most common problem is that it reports that there is no such table. And if that's what you see, in this case that could be a result of failing to include the database in the app bundle (see the "Copy Bundle Resources" section of the "Build Phases" section of your target settings).
Alternatively, if it says something about the database is busy, that can happen if you have mismatched your database open calls and your database close calls (like you have in this code sample).

please make sure your sqlite db is not opened with any sqlite db browser when you are running app in simulator.
It happened with me, I had my sqlite db opened in sqlite browser to test my query and when I was running app from simulator it was not updating anything in db.

The problem was my file was of sqlite extension, but the code specified an extension of sqlite3. I thought the two were synonymous but they weren't

Related

How to view the sqlite db in the application installed in iOS for testing

I have installed my application in the simulator and need to view the DB. Please tell me the application to view this or can I view it from Xcode itself.
DATABASE
//database connection
con = [[DataBase alloc]init];
NSError *error = [[NSError alloc]init];
NSFileManager *filemanager = [NSFileManager defaultManager];
NSArray *arryPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *DocumentPath = [arryPath objectAtIndex:0];
NSString *strDocumentPath = [DocumentPath stringByAppendingPathComponent:#"School.sqlite"];
// check file is exist or not
int success = [filemanager fileExistsAtPath:strDocumentPath];
//if file not exist at path
if (!success) {
NSLog(#"SchoolName is: %#",#"No Database exist");
NSString *strDefaultPath = [[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:#"School.sqlite"];
success = [filemanager copyItemAtPath:strDefaultPath toPath:strDocumentPath error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
//file exist at path
if (success) {
NSLog(#"SchoolName is: %#",#" Database exist");
if (sqlite3_open([strDocumentPath UTF8String],&database) == SQLITE_OK) {
} else {
sqlite3_close(database);
NSAssert1(0, #"Failed to open database with message '%s'.", sqlite3_errmsg(database));
}
}
Install DB Browser for Sqlite in Mac.
Provide path of .sqlite in documents directory of app to DB browser
It'll open live db from simulator
All entities of Core Data will have ZTablename naming convention
Keep refreshing DB browser for updated data during testing
Try to use the client e.g. from here http://sqlitebrowser.org/ and open sqlite file.
If you installed firefox on your PC, use "SQLite Manager" add-on is quite good, it's easy, lightly, and free. I've used it before to manage my sqlite db with ~40k records with no problem.
https://addons.mozilla.org/en-US/firefox/addon/sqlite-manager/
For live database Changes please try this:
1.Install DBBrowser for SQLite.
2.Log your strDocumentPath in console using NSLog And copy the path.
3.Open DBBrowser and open database and in finder press CMD+SHIFT+G and paste your link there and press ENTER. You will be redirected to that location.
4.Select your SQLite file and it will be opened in DBBrowser. And keep it there until you are done with your DB works.
Now whenever you refresh the database from DBBrowser you will get the recent changes in DB also any manual changes in DB will be effected in your Simulator.
Here is a way I do that
init() {
do {
// Get database path
if let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first {
// Connect to database
db = try Connection("\(path)/db.sqlite3")
// Initialize tables
try db?.run(rssNewspaper.create(ifNotExists: true) { table in
table.column(rssNewspaperId, primaryKey: .autoincrement)
table.column(rssNewspaperName)
table.column(rssIsActive)
})
}
} catch {
print(error.localizedDescription)
}
}
point debuger point in path and when deice is locked there print to path then open terminal and use this command "open (path/of/printed/path)" in between( ) past the printed value it will be open

Keychain iOS not always storing values

I´m developing a couple of iOS applications and i need to share a element between them, that i want to store in the keychain.
This element is used in a complex login process with 3 or 4 steps, in each one i need to read the value from the keychain, to do this i used the code bellow:
- (NSString *)installationToken
{
KeychainItemWrapper *kw = [[KeychainItemWrapper alloc] initWithIdentifier:#"uuid" accessGroup:#"yyyyy.xxxxxxxxxxx"];
if (![kw objectForKey:(NSString*)kSecAttrAccount] || [[kw objectForKey:(NSString*)kSecAttrAccount] isEqualToString:#""]) {
NSString *result;
CFUUIDRef uuid;
CFStringRef uuidStr;
uuid = CFUUIDCreate(NULL);
assert(uuid != NULL);
uuidStr = CFUUIDCreateString(NULL, uuid);
assert(uuidStr != NULL);
result = [NSString stringWithFormat:#"%#", uuidStr];
assert(result != nil);
CFRelease(uuidStr);
CFRelease(uuid);
[kw setObject:result forKey:(NSString*)kSecAttrAccount];
return result;
} else {
return [kw objectForKey:(NSString*)kSecAttrAccount];
}
}
This all works well in almost every device but in some, users are complaining. So, i checked what my server is receiving, and saw that different values are being sent.
I checked the code and in no other place i'm acessing/emptying this keychain element, what can be wrong with this? For the majority of devices this works like a charm but for some reason, in some devices, they aren't storing or retrieving well from the keychain.
The problem happens in different invocation in the same application.
If you are using Apples' sample code for KeyChainWrapper, then main problem is sometimes randomly, SecItemCopyMatching fails and then the sample code has resetKeychainItem which will basically reset your keychain.
if (! SecItemCopyMatching((CFDictionaryRef)tempQuery, (CFTypeRef *)&outDictionary) == noErr)
{
// Stick these default values into Keychain if nothing found.
[self resetKeychainItem];
}
In our app, we noticed similar problems, and so now we are using
https://github.com/carlbrown/PDKeychainBindingsController to do all keyChain related functionality. Now it works very well.

SQLite Persistence project : errorMsg

I use errorMsg in my NSAssert, but I only defined it as NULL and never used it to get the actual error message. So, it will always be NULL and there is no point to use it in NSAssert.
<...>
char *errorMsg = NULL;
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(database, update, -1, &stmt, nil)
== SQLITE_OK) {
sqlite3_bind_int(stmt, 1, i);
sqlite3_bind_text(stmt, 2, [field.text UTF8String], -1, NULL);
}
if (sqlite3_step(stmt) != SQLITE_DONE)
NSAssert(0, #"Error updating table: %s", errorMsg);
<...>
will anyone give a solution?
when I run the app, there is no harm. but then, when I press the home button, the process paused and shows me this:
2013-05-20 23:57:50.156 SQLite Persistence[5373:c07] * Assertion failure in -[LPEViewController applicationWillResignActive:], /Users/Me/Developer/SQLite Persistence/SQLite Persistence/LPEViewController.m:84 2013-05-20 23:57:50.158 SQLite Persistence[5373:c07] Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Error updating table: (null)' ** First throw call stack: (0x2094012 0x11a1e7e 0x2093e78 0xc37665 0x3c09 0xc624f9 0x20ee0c5 0x2048efa 0xb96bb2 0xe2bb1 0xe2c3d 0xece0c 0xf5e74 0xf6beb 0xe8698 0x1fefdf9 0x1fefad0 0x2009bf5 0x2009962 0x203abb6 0x2039f44 0x2039e1b 0x1fee7e3 0x1fee668 0xe5ffc 0x2b4d 0x2a75) libc++abi.dylib: terminate called throwing an exception (lldb)
A couple of issues:
You never set the errorMsg. Make sure to set it to sqlite3_errmsg (or just use that function directly).
Your custom error message ("Error updating table") is a little misleading, too, as it would imply that you're reporting the name of a table, whereas your choice of variable name suggested you really wanted to report the SQLite error message.
If sqlite3_prepare_v2 fails, you don't report any error message. Furthermore, rather than stopping and reporting an error if sqlite3_prepare_v2 failed, you proceed to try to call sqlite3_step, even though there's no valid statement to perform. The problem with that is that it would undoubtedly replace the meaningful error message you would have received after sqlite3_prepare_v2 failed with some useless message about executing statements in the wrong order.
You don't check the success or failure of your sqlite3_bind statements. It would be prudent to do so (though I suspect you're more likely to fail at the sqlite3_prepare_v2 statement).
Anyway, maybe you want something like:
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(database, update, -1, &stmt, nil) != SQLITE_OK)
NSAssert(0, #"prepare failure: %s", sqlite3_errmsg(database));
if (sqlite3_bind_int(stmt, 1, i) != SQLITE_OK) {
sqlite3_finalize(stmt);
NSAssert(0, #"bind 1 failure: %s", sqlite3_errmsg(database));
}
if (sqlite3_bind_text(stmt, 2, [field.text UTF8String], -1, NULL) != SQLITE_OK) {
sqlite3_finalize(stmt);
NSAssert(0, #"bind 2 failure: %s", sqlite3_errmsg(database));
if (sqlite3_step(stmt) != SQLITE_DONE) {
sqlite3_finalize(stmt);
NSAssert(#"step error: %s", sqlite3_errmsg(database));
}
sqlite3_finalize(stmt);
Whether you want to use NSAssert or just NSLog and immediately return, I'll defer to you, but this code sample will check more SQLite failure conditions and report meaningful errors.

SQLite + SQLCipher + FMDatabase on iOS

I'm currently using FMDatabase in my iOS app and I'm very happy with it.
I plan to encrypt the sqlite database with SQLCipher.
Here are my questions:
1) Are FMDatabase and SQLCipher compatibles ? I think I just need to add a new method in FMDatabase called openEncrypted...and do the job for SQLCipher. I hope all FMDatabase methods will work.
2) Actually, I have 2 databases in my app. Then I do an ATTACH DATABASE in my app to join them. I would like to encrypt just one of the two. Will it work or I need to encrypt the 2 databases ? (One is critical, the other is not)
3) I don't really understand what I will have to provide to Apple (documents) if I encrypt these files.
Thanks you !
For those looking for a simple tutorial on how to accomplish this, I was able to create one: http://www.guilmo.com/fmdb-with-sqlcipher-tutorial/
But the most important parts are, Opening your existing DB and attaching a new encrypted one. Then setting the key in your FMDB connections.
SQLCipher - Encrypting the database
// Import sqlite3.h in your AppDelegate
#import <sqlite3.h>
// Set the new encrypted database path to be in the Documents Folder
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDir = [documentPaths objectAtIndex:0];
NSString *ecDB = [documentDir stringByAppendingPathComponent:#"encrypted.sqlite"];
// SQL Query. NOTE THAT DATABASE IS THE FULL PATH NOT ONLY THE NAME
const char* sqlQ = [[NSString stringWithFormat:#"ATTACH DATABASE '%#' AS encrypted KEY 'secretKey';",ecDB] UTF8String];
sqlite3 *unencrypted_DB;
if (sqlite3_open([self.databasePath UTF8String], &unencrypted_DB) == SQLITE_OK) {
// Attach empty encrypted database to unencrypted database
sqlite3_exec(unencrypted_DB, sqlQ, NULL, NULL, NULL);
// export database
sqlite3_exec(unencrypted_DB, "SELECT sqlcipher_export('encrypted');", NULL, NULL, NULL);
// Detach encrypted database
sqlite3_exec(unencrypted_DB, "DETACH DATABASE encrypted;", NULL, NULL, NULL);
sqlite3_close(unencrypted_DB);
}
else {
sqlite3_close(unencrypted_DB);
NSAssert1(NO, #"Failed to open database with message '%s'.", sqlite3_errmsg(unencrypted_DB));
}
self.databasePath = [documentDir stringByAppendingPathComponent:#"encrypted.sqlite"];
Note that we set 2 parameters in SQL Query, the DATABASE and the KEY. The DATABASE should be the full path to the encrypted database you want to create, in this case, string ecDB, and the KEY parameter is the key that’s going to be use to ENCRYPT your database, so choose a strong one
Now on your FMDB functions, call [db setKey:#"strongKey"] after every time you open the db.
// FMDatabase Example
FMDatabase *db = [FMDatabase databaseWithPath:[self getDatabasePath]];
[db open];
[db setKey:#"secretKey"];
// FMDatabaseQueue Exmple
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:[self getDatabasePath]];
[queue inDatabase:^(FMDatabase *db) {
[db setKey:#"secretKey"];
...
}];
Let me know if you have any questions!
Yes, You still need to compile in SQLCipher, but you can use FMDB. FMDB also provides a function to set the encryption key for the database
You can attach a non-encrypted database to an encrypted database (see http://sqlcipher.net/sqlcipher-api/#attach)
You will generally need to do an encryption registration with the DOC and then self-classify as mass market http://www.bis.doc.gov/encryption/question4.htm
Hi I am using swift and following is the code I follow. But I have one problem the encrypted.sqlite file I can open with SQLiteBrowser what I was wrong here.
var db: COpaquePointer = nil;
let databasePath = FileUtils.getPath("data.db")
var ecDB = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0].stringByAppendingPathComponent("encrypted.sqlite")
let result = String.fromCString("ATTACH DATABASE \(ecDB) AS encrypted KEY TaP")
if (sqlite3_open(databasePath, &db) == SQLITE_OK) {
sqlite3_exec(db, result!, nil, nil, nil);
sqlite3_exec(db, "SELECT sqlcipher_export('encrypted');", nil, nil, nil);
sqlite3_exec(db, "DETACH DATABASE encrypted;", nil, nil, nil);
sqlite3_close(db);
}
else {
sqlite3_close(db);
sqlite3_errmsg(db);
}

SQLite3 error - iOS

How can I figure out the error that SQLite3 is giving me when I make an SQL call:
int success = sqlite3_prepare_v2(database, sql, -1, &stmt, NULL);
if(success != SQLITE_OK) {
NSLog(#"create stmt failed %#",stmt);
}
All as I know is if it failed, but is there a way to get the actual error, or reason why it failed?
You can use the sqlite3_errmsg function. You need to pass in the db handle. The following code will log the error
NSLog(#"Error %s while preparing statement", sqlite3_errmsg(_dbHandle));
Sure, if success != SQLITE_OK, then it must be one of these error codes:
http://www.sqlite.org/c3ref/c_abort.html

Resources