I've added FMDB/SQLCipher (2.7.5) to my app by cocoapods.
use_frameworks!
pod 'FMDB/SQLCipher'
And perform the code bellow:
- (void)decryptDBAtPath:(NSString *)path withKey:(NSString *)key {
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:path];
[queue inTransaction:^(FMDatabase * _Nonnull db, BOOL * _Nonnull rollback) {
BOOL result = [db setKey:key];
NSLog(#"decrypt db %#", result ? #"success" : #"failed");
//it always return YES.
NSString *sql = #"SELECT * FROM PTypeImage";
FMResultSet *rs = [db executeQuery:sql];
NSDictionary *dic = [rs resultDictionary];
NSLog(#"query test with result : %#", dic);
//query can not by executed. 'file is not a database'.
}];
}
It's always return decrypt success, but query failed.
Any wrong in my code?
I also tryed encryption by FMDB. It seems failed. Returns encrypt success, but can be open without key.
- (void)enryptDBAtPath:(NSString *)path withKey:(NSString *)key {
FMDatabase *db = [FMDatabase databaseWithPath:path];
if ([db open]) {
BOOL result = [db rekey:key];
//always return YES.
NSLog(#"encrypt db %#", result ? #"success" : #"failed");
[db close];
}
}
Related
I've an application when users can save their ECG. I want to split Realm usage by user. I'm developing a user.realm (referenced as _realmUsers) to save all the users that login on application, and a userID.realm (referenced as _realm and They will be once per user that login on application) where user saves his data. How to achieve that? Because if I write things like
Initialization of both Realms
+ (void)initializeRealmForUsers
{
// Open the encrypted Realm file
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.fileURL = [[[config.fileURL URLByDeletingLastPathComponent]
URLByAppendingPathComponent:USER_REALM]
URLByAppendingPathExtension:#"realm"];
NSError *err;
RLMRealm *realmUser = [RLMRealm realmWithConfiguration:config error:&err];
if(err == nil && realmUser != nil)
[sharedInstance setRealmUsers:realmUser];
}
+ (void)initializeDataRealmWithUserID:(NSString*)userUUID
{
// Open the encrypted Realm file
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.fileURL = [[[config.fileURL URLByDeletingLastPathComponent]
URLByAppendingPathComponent:userUUID]
URLByAppendingPathExtension:#"realm"];
NSError *err;
RLMRealm *realmUser = [RLMRealm realmWithConfiguration:config error:&err];
if(err == nil && realmUser != nil)
[sharedInstance setRealmUsers:realmUser];
[[RealmManager sharedInstance] deleteIncompleteExam];
}
Creation of User:
- (User *)createUserWithData:(NSDictionary *)data
{
[_realmUsers beginWriteTransaction];
User *newUser = [User new];
[newUser setEmail:[data objectForKey:USER_EMAIL]];
[newUser setPassword:[CryptoUtils MD5String:[data
objectForKey:USER_PASSWORD]]];
[newUser setLogged:#NO];
[_realmUsers addOrUpdateObject:newUser];
[_realmUsers commitWriteTransaction];
return newUser;
}
Creation of Patient, a record type of the second realm (the specific-user one):
- (Patient *)createPatientWithData:(NSDictionary *)data forUser:(BOOL)forUser
{
Patient *newPatient;
if([[RealmManager sharedInstance] getSelfPatient] == nil)
newPatient = [Patient new];
else
newPatient = [[RealmManager sharedInstance] getSelfPatient];
[_realm beginWriteTransaction];
[newPatient setFirstName:[data objectForKey:PATIENT_FIRSTNAME]];
[newPatient setLastName:[data objectForKey:PATIENT_LASTNAME]];
[newPatient setSelfPatient:forUser];
[_realm addOrUpdateObject:newPatient];
[_realm commitWriteTransaction];
return newPatient;
}
and Login:
- (User *)loginWithUsername:(NSString *)email password:(NSString *)password andData:(NSDictionary *)userData
{
User *user = [self userWithUsername:email];
if(user == nil){
NSMutableDictionary *mutableData = [NSMutableDictionary new];
[mutableData addEntriesFromDictionary:userData];
[mutableData setObject:password forKey:USER_PASSWORD];
user = [self createUserWithData:mutableData];
} else{
[_realmUsers beginWriteTransaction];
[user setPassword:[CryptoUtils MD5String:password]];
[_realmUsers commitWriteTransaction];
}
[_realmUsers beginWriteTransaction];
[user setLogged:#YES];
[_realmUsers commitWriteTransaction];
[RealmManager initializeDataRealmWithUserID:[user uuid]];
[self createPatientWithData:#{PATIENT_FIRSTNAME : [userData objectForKey:PATIENT_FIRSTNAME], PATIENT_LASTNAME : [userData objectForKey:PATIENT_LASTNAME]} forUser:YES];
return user;
}
After Login, my Calls Like [User allObjects] (user must be on user.realm) [Patient allObject](patients must be on userID.realm) gives me empty result.
You can access datas from different realm by
[User allObjectsInRealm: user.realm]
[User objectsInRealm:user.realm where:#""];
Here is the code which I'm using to insert a record into a sqlite table
FMDatabase *dataBase = [self openDatabase];
[dataBase open];
if ([dataBase open] != YES) {
NSLog(#"DB Error %d: %#", [dataBase lastErrorCode], [dataBase lastErrorMessage]);
//VERY IMPORTANT
}
BOOL success= [dataBase executeUpdate:#"Insert into CrewUnits (pkCrewUnits,fkUnits,fkAgencyVehicles,fkLookupCodes_PrimaryRole, fkLookupCodes_LevelOfCare, PrimaryUnit) values (?, ?, ?, ?, ?, ?);", pkCrewUnits, fkUnits, fkAgencyVehicle, fkLookupCodes_PrimaryRole, fkLookupCodes_LevelOfCare, [NSNumber numberWithInt:1]];
NSLog(success ?#"YES" :#"NO");
NSLog(#"Error %d: %#", [dataBase lastErrorCode], [dataBase lastErrorMessage]);
FMResultSet*resultSet= [dataBase executeQuery:#"select * from CrewUnits"];
NSLog(#"ResultSet : %#", [resultSet resultDictionary]);
[dataBase close];
Database Path
- (FMDatabase *)openDatabase
{
NSLog(#"Open Database");
NSString *documents_dir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *db_path = [documents_dir stringByAppendingPathComponent:[NSString stringWithFormat:#"HPSix_05BD.db"]]; // DatabasePath
FMDatabase *db = [FMDatabase databaseWithPath:db_path];
if (![db open])
NSLog(#"Failed to open database!!!!!");
return db;
}
I'm using the same logic to fetch data from table that works fine for me. But I can't insert a record. I don't know what I'm doing wrong here.
Two problems:
You are calling open three times. Once in openDatabase and twice in the code that called it.
So, have your open routine open the database if it can, but return nil if it can't:
- (FMDatabase *)openDatabase
{
NSLog(#"Open Database");
NSString *documents_dir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *db_path = [documents_dir stringByAppendingPathComponent:[NSString stringWithFormat:#"HPSix_05BD.db"]]; // DatabasePath
FMDatabase *db = [FMDatabase databaseWithPath:db_path];
if (![db open]) {
NSLog(#"Failed to open database!!!!!");
return nil;
}
return db;
}
Then check that result:
FMDatabase *dataBase = [self openDatabase];
if (!dataBase) {
// quit; note, no point in checking error message as it only returns meaningful messages if you have an open database
}
After calling executeQuery, you have to call next.
FMResultSet *resultSet = [dataBase executeQuery:#"select * from CrewUnits"];
if ([resultSet next]) {
NSLog(#"ResultSet : %#", [resultSet resultDictionary]);
} else {
NSLog(#"No data found");
}
I think I see the problem. You are committing after running the SELECT query.
You have to commit right after the UPDATE query. When you do it like you have now when selecting, the UPDATE query has not been committed and thus will not be returned.
[dataBase beginTransaction];
BOOL success= [dataBase executeUpdate:#"Insert into CrewUnits (pkCrewUnits,fkUnits,fkAgencyVehicles,fkLookupCodes_PrimaryRole, fkLookupCodes_LevelOfCare, PrimaryUnit) values (?, ?, ?, ?, ?, ?);", pkCrewUnits, fkUnits, fkAgencyVehicle, fkLookupCodes_PrimaryRole, fkLookupCodes_LevelOfCare, [NSNumber numberWithInt:1]];
[dataBase commit]; // commit the query
NSLog(success ?#"YES" :#"NO");
NSLog(#"Error %d: %#", [dataBase lastErrorCode], [dataBase lastErrorMessage]);
// now at this point the transaction has been committed and can be selected
FMResultSet*resultSet= [dataBase executeQuery:#"select * from CrewUnits"];
NSLog(#"ResultSet : %#", [resultSet resultDictionary]);
Also according to FMDB docs:
The parameters must start with a colon. SQLite itself supports other
characters, but internally the Dictionary keys are prefixed with a
colon, do not include the colon in your dictionary keys.
Thus this line of code must be rewritten to:
BOOL success= [dataBase executeUpdate:#"Insert into CrewUnits (pkCrewUnits,fkUnits,fkAgencyVehicles,fkLookupCodes_PrimaryRole, fkLookupCodes_LevelOfCare, PrimaryUnit) values (:pkCrewUnits,:fkUnits,:fkAgencyVehicles,:fkLookupCodes_PrimaryRole, :fkLookupCodes_LevelOfCare, :PrimaryUnit);", pkCrewUnits, fkUnits, fkAgencyVehicle, fkLookupCodes_PrimaryRole, fkLookupCodes_LevelOfCare, [NSNumber numberWithInt:1]];
Lastly according to the docs you use next for looping through the results:
while ([resultSet next]) {
//retrieve values for each record
}
I have one method which accepts va_list like this:
+(NSUInteger) addObjectToDB:(NSString*)dbFilePath withSQL:(NSString*)sql, ... {
va_list args;
va_start(args, sql);
FMDatabaseQueue* dbQ = [FMDatabaseQueue databaseQueueWithPath:dbFilePath];
__block NSUInteger result = -1;
[dbQ inDatabase:^(FMDatabase *db) {
[db open];
if ([db executeUpdate:sql, args]) {
result = (NSUInteger) [db lastInsertRowId];
}
[db close];
}];
va_end(args);
return result;
}
EDIT1:
I want to pass va_list(args) to the 'executeUpdate:' method of FMDatabase like this:
NSString* sql = [NSString stringWithFormat:#"Insert Into Table_Name Values (NULL, '%#', ?, ?)", #"string"];
[CXDBHelper addObjectToDB:self.dbFilePath withSQL:sql, NSData, NSData];
but the execution stopped in the method (FMDatabase.m) :
- (void)bindObject:(id)obj toColumn:(int)idx inStatement:(sqlite3_stmt*)pStmt {
if ((!obj) || ((NSNull *)obj == [NSNull null])) {
sqlite3_bind_null(pStmt, idx);
}
// FIXME - someday check the return codes on these binds.
else if ([obj isKindOfClass:[NSData class]]) {
at the last line without error message.
EDIT 2:
Thanks to #rmaddy's tips, I check the code executeUpdate:, it is defined as:
- (BOOL)executeUpdate:(NSString*)sql, ...
I realize that the problem is I cannot pass a va_list to it.
EDIT 3:
At last, I use another method which accept va_list: executeUpdate: withVAList: and everything is OK now.
I am unable to delete the tables from sqlite database using DROP TABLE query. Have tried all possible solutions such as [database openCloseResultSet], [resultSet close] and so on. But adding these lines results in out of memory error and if i dont write these lines then my program just stops at the drop table statement. There is no warning or error given too.Even if i put the code in debug, my debug point just goes away once this line is executed.I am using FMDatabase library while working with process related to sqlite.I am unable to find out the cause of this issue.
Have tried these links too but they are not working in my case.
1)FMDB executeUpdate DROP command does halt the app
2)How to remove all data from table using FMDB
Here is my code where the issue prevails.
+ (NSString *) deleteTable:(NSInteger) index{
NSString *returnFlag = #"success";
FMDatabase *database = nil;
#try {
NSString *query = #"select name from sqlite_master where type = 'table'";
database = [FMDatabase databaseWithPath:[DBOperator getDataBasePath:DATABASENAME]];
if(database != nil && [database open])
{
FMResultSet *resultSet = [database executeQuery:query];
while ([resultSet next]) {
NSString *temp = [resultSet stringForColumnIndex: 0];
if (index == DELETE_TYPE_ONE &&
([temp equalsIgnoreCase: TBUPW] ||
[temp equalsIgnoreCase: TBCVR] ||
[temp equalsIgnoreCase: TBCNTRLNO])) {
[database executeUpdate:[NSString stringWithFormat:#"DROP TABLE %#", temp]];
}else if(index == DELETE_TYPE_TWO && [temp hasPrefix:#"txn"]){
[database executeUpdate:[NSString stringWithFormat:#"DROP TABLE %#", temp]];
}else if(index == DELETE_TYPE_THREE &&
([temp hasPrefix:#"t"] ||
[temp hasPrefix:#"T"] ||
[temp hasPrefix:#"ob"] ||
[temp hasPrefix:#"fb"] ||
[temp hasPrefix:#"cp"])){
NSlog("This is printed in console");
[database executeUpdate:[NSString stringWithFormat:#"DROP TABLE %#", temp]];
NSLog("This is not printed in console");
}else if(index == DELETE_TYPE_FOUR && [temp equalsIgnoreCase:#"TBPLAN"]){
[database executeUpdate:[NSString stringWithFormat:#"DROP TABLE %#", temp]];
}
}
}
}
#catch (NSException *exception) {
returnFlag = #"error";
}
#finally {
[database close];
}
return returnFlag;
}
you can use this query.
DROP TABLE YOUR_TABLE_NAME;
or you can use this
DELETE FROM YOUR_TABLE_NAME;
As for me following code works, maybe it'll give you a hint:
#property (nonatomic, strong, readonly) FMDatabaseQueue *database;
__block BOOL finishedSuccessfully = YES;
[database inTransaction:^(FMDatabase *db, BOOL *rollback) {
// firstly let's remove the table:
[db closeOpenResultSets];
FMResultSet *deleteResultSet = [db executeQuery:#"DROP TABLE myTableName"];
if ([deleteResultSet next]) {}
[deleteResultSet close];
finishedSuccessfully = !db.lastErrorCode && finishedSuccessfully; // breakpoint after this line
// then let's perform anything on the db:
FMResultSet *select = [db executeQuery:#"SELECT row FROM myTableName WHERE anotherRow = ?", #{2}];
finishedSuccessfully = !db.lastErrorCode && finishedSuccessfully; // breakpoint after this line
}
I don't have an error after the first part, but second part returns something like "there is no such table as myTableName".
Im having an issue deleting all the rows in a sqlite table I created, I can delete from this table based on a condition, (name, date etc) however when I try to delete all the contents in the table it fails: Here is what I've tried:
-(void) deleteFromDB{
NSLog(#"delete from DB method being called");
NSString * deleteQuery = #"DELETE * FROM CLUB";
[self deleteData:deleteQuery];
}
-(void)deleteData:(NSString *)deleteQuery{
char *error;
NSLog(#"Enter deleteData");
if (sqlite3_exec(clubDB, [deleteQuery UTF8String], NULL, NULL, &error)==SQLITE_OK) {
NSLog(#"CLUB info deleted");
}else{
NSLog(#"Failed");
NSLog(#"Failed with %s", error);
}
}
I get an output of failed.
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Can't Delete! %# %#", error, [error localizedDescription]);
return;
}
cant delete is never printed
The error message is:
X”¿
*Update**
clubDB is of type sqlite3 (declared in .h as sqlite3*clubDB
error string updated above
**Update2*****
Here is the code for opeing/creating the database:
-(void) createOrOpenDB{
NSArray * path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * docPath = [path objectAtIndex:0];
dbPathString = [docPath stringByAppendingPathComponent:#"club.db"];
NSLog(#"path = %#",dbPathString);
char*error;
NSFileManager*fileManager = [NSFileManager defaultManager];
if(![fileManager fileExistsAtPath:dbPathString]){
const char * dbPath = [dbPathString UTF8String];
//create db here
if (sqlite3_open(dbPath, &clubDB)==SQLITE_OK) {
NSLog(#"success");
const char *sql_stmt = "CREATE TABLE IF NOT EXISTS CLUB (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT, SUBJECT TEXT)";
sqlite3_exec(clubDB, sql_stmt, NULL, NULL, &error);
sqlite3_close(clubDB);
}
}
}
Your delete statement isn't valid SQL. To delete everything from CLUB it would be:
delete from CLUB
You don't need the star as it obviously affects the whole record.