I'm working on my app that let me to store my online DVD's information on my iphone. Just wonder to use localStorage or SQLite. Also I'm gonna update my app in the future and I dont want to loose my stored data. Which one do you suggest me ?!
Cheers
my online DVD's information
can be large information, then use core data or SQLite.
Also I'm gonna update my app in the future and I dont want to loose my
stored data
if you update without deleting the old version, SQLite data remains. This works same for NSUserDefault too. You can use Keychains for permanant data storage, but for small amount of data. Can't say more without knowing more about your requirement.
I do not know much about localStograge anyway SQLite will give you good options:
Portable to Android
Can be updated without losing your previous database.
here is a sample code to init DB and upgrade it.
+(void) InitalizeDB
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSString *dbPath = [self getDBPath];
BOOL success = [fileManager fileExistsAtPath:dbPath];
if(!success)
{
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"QuestionsDB.sqlite"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:dbPath error:&error];
if (!success)
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
if (success)
{
if (sqlite3_open([dbPath UTF8String], &gDatabase) != SQLITE_OK)
{
sqlite3_close(gDatabase);
gDatabase = nil;
}
}
if (gDatabase != nil)
[self Upgrade];
}
+(void)Upgrade
{
sqlite3 *NewDatabase = nil;
NSString *NewDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"QuestionsDB.sqlite"];
const char *szVersion = "SELECT DBVersion FROM Version";
sqlite3_stmt *sqlOldVersion;
sqlite3_stmt *sqlNewVersion;
if (sqlite3_open([NewDBPath UTF8String], &NewDatabase) != SQLITE_OK)
{
sqlite3_close(NewDatabase);
NewDatabase = nil;
}
if (NewDatabase != nil)
{
if (sqlite3_prepare_v2(gDatabase, szVersion, -1, &sqlOldVersion, NULL) != SQLITE_OK)
NSAssert1(0, #"Failed to compile sql statment %s", sqlite3_errmsg(gDatabase));
if (sqlite3_prepare_v2(NewDatabase, szVersion, -1, &sqlNewVersion, NULL) != SQLITE_OK)
NSAssert1(0, #"Failed to compile sql statment %s", sqlite3_errmsg(NewDatabase));
if (sqlite3_step(sqlOldVersion) == SQLITE_ROW && sqlite3_step(sqlNewVersion) == SQLITE_ROW)
{
if (sqlite3_column_int(sqlOldVersion, 0) < sqlite3_column_int(sqlNewVersion, 0))
{
[self UpgradeCategories:NewDatabase];
[self UpgradeQuestions:NewDatabase];
[self UpgradeQuestionChoices:NewDatabase];
}
}
else
{
NSAssert(0, #"Failed to retreive questions database version.");
}
}
sqlite3_close(NewDatabase);
}
Related
I have Sequence of Queries that need to be performed with database..
Most of time its working fine.. but some time it failed to insert query.
if (sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
for (int i=0;i<[queries count]; i++)
{
NSString *query = [queries objectAtIndex:i];
const char *Insert_query = [query UTF8String];
sqlite3_prepare(contactDB, Insert_query, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
//NSLog(#" \n\n\n\n %# done query",query);
}
else {
NSLog(#" \n\n\n\n %# not done query",query);
}
}
sqlite3_finalize(statement);
sqlite3_close(contactDB);
}
Above is code which i have implemented to perform insert operation...
Can any one help me to find if it fails then for what reason it failed to insert to database so i can handle error..
Use this method to execute query on sqlite
//-----------------------------------------------------------------------------------------------------//
#pragma mark - Helper methods
//-----------------------------------------------------------------------------------------------------//
-(BOOL)dbOpenedSuccessfully
{
if(sqlite3_open([[self dbPath] UTF8String], &_database) == SQLITE_OK)
{
return YES;
}
else
{
[[[UIAlertView alloc]initWithTitle:#"Error"
message:#"Error on opening the DB"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil, nil]show];
return NO;
}
}
//-----------------------------------------------------------------------------------------------------//
#pragma mark - Query
//-----------------------------------------------------------------------------------------------------//
- (void) executeQuery:(NSString *)strQuery
{
char *error = NULL;
if([self dbOpenedSuccessfully])
{
NSLog(#"%#",strQuery);
sqlite3_exec(_database, [strQuery UTF8String], NULL, NULL,&error);
if (error!=nil) {
NSLog(#"%s",error);
}
sqlite3_close(_database);
}
}
Also If insert not works properly the reason may be the file is not in the documents directory and if it is there in bundle it will fetch tha data but cannot update or insert value if db is in bundle ,Copy it to the documents directory and then try using it
-(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:_databasePath error:nil];
}
For more info see this
You can try printing the error in the following way, Based on the error you can make decision.
if (sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
for (int i=0;i<[queries count]; i++)
{
NSString *query = [queries objectAtIndex:i];
const char *Insert_query = [query UTF8String];
sqlite3_prepare(contactDB, Insert_query, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
//NSLog(#" \n\n\n\n %# done query",query);
}
else {
NSLog(#"sqlite3_step error: '%s'", sqlite3_errcode(contactDB));
NSLog(#" \n\n\n\n %# not done query",query);
}
}
sqlite3_finalize(statement);
sqlite3_close(contactDB);
}
Additionally,
SQLITE_DONE means that the statement has finished executing
successfully. sqlite3_step() should not be called again on this
virtual machine without first calling sqlite3_reset() to reset the
virtual machine back to its initial state.
You can use, SQLITE_OK instead of SQLITE_DONE.
I'm getting my data ,with several similar methods, from sqlite3 file like in following code:
-(NSMutableArray *) getCountersByID:(NSString *) championID{
NSMutableArray *arrayOfCounters;
arrayOfCounters = [[NSMutableArray alloc] init];
#try {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *databasePath = [[[NSBundle mainBundle] resourcePath ]stringByAppendingPathComponent:#"DatabaseCounters.sqlite"];
BOOL success = [fileManager fileExistsAtPath:databasePath];
if (!success) {
NSLog(#"cannot connect to Database! at filepath %#",databasePath);
}
else{
NSLog (#"SUCCESS getCountersByID!!");
}
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK){
NSString *tempString = [NSString stringWithFormat:#"SELECT COUNTER_ID FROM COUNTERS WHERE CHAMPION_ID = %#",championID];
const char *sql = [tempString cStringUsingEncoding:NSASCIIStringEncoding];
sqlite3_stmt *sqlStatement;
int ret = sqlite3_prepare(database, sql, -1, &sqlStatement, NULL);
if (ret != SQLITE_OK) {
NSLog(#"Error calling sqlite3_prepare: %d", ret);
}
if(sqlite3_prepare_v2(database, sql, -1, &sqlStatement, NULL) == SQLITE_OK){
while (sqlite3_step(sqlStatement)==SQLITE_ROW) {
counterList *CounterList = [[counterList alloc]init];
CounterList.counterID = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement,0)];
[arrayOfCounters addObject:CounterList];
}
}
else{
NSLog(#"problem with database prepare");
}
sqlite3_finalize(sqlStatement);
}
else{
NSLog(#"problem with database openning %s",sqlite3_errmsg(database));
}
}
#catch (NSException *exception){
NSLog(#"An exception occured: %#", [exception reason]);
}
#finally{
sqlite3_close(database);
return arrayOfCounters;
}
//end
}
then i'm getting access to data with this and other similar lines of code:
myCounterList *MyCounterList = [[myCounterList alloc] init];
countersTempArray = [MyCounterList getCountersByID:"2"];
[countersArray addObject:[NSString stringWithFormat:#"%#",(((counterList *) [countersTempArray objectAtIndex:i]).counterID)]];
I'm getting a lot of data like image name and showing combination of them that depends on users input with such code:
UIImage *tempImage = [UIImage imageNamed:[NSString stringWithFormat:#"%#_0.jpg",[countersArray objectAtIndex:0]]];
[championSelection setBackgroundImage:tempImage forState:UIControlStateNormal];
My problem:
When i'm run my app for some time and get a lot of data it throws error: " problem with database openning unable to open database file - error = 24 (Too many open files)"
My guess is that i'm opening my database every time when getCountersByID is called but not closing it.
My question:
Am i using right approach to open and close database that i use?
Similar questions that did not helped me to solve this problem:
unable to open database
Sqlite Opening Error : Unable to open database
UPDATE:
I made assumption that error is showing up because i use this lines of code too much:
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *databasePath = [[[NSBundle mainBundle] resourcePath ]stringByAppendingPathComponent:#"DatabaseCounters.sqlite"];
BOOL success = [fileManager fileExistsAtPath:databasePath];
and ending up with error 24.
So i made them global but sqlite3_errmsg shows same err 24, but app runs much faster now
You should open your DB only once basically when you are in the initialization phase but not when you are requesting some info to your DB. Your code shouldn't failed though since you seems to open then close the DB after each request. Make sure this is happening by either logging those events or debugging through your code.
The code you've shown does close the database, so it's likely that you forget to close it in some other place, or that some other file is opened repeatedly but never closed.
I have an application where I have two textfields and a button. In the first textfield I am entering the username of the user and in the second textfield I am entering the password of the user. When the user enters the username and password and click on create user button a backend api is called which will register the username and password of the user and at the time I want to enter the username and password of the user in local SQLite databse in encrypted format.
When a user is registered the username of that particular user should get displayed in the first textfield and it should not be editable and password field should be editable. Only one user is allowed to be registered for the entire application.
Don't use a sqlite dbs to store user credentials. With reverse engineering it is fairly simple to get to the data. This because you probably have some value in your program you use to encrypt/decrypt the password. For security purposes, please use the keychain. There are several projects on github which makes it very easy to use the keychain. There is no more secure way for saving credentials then the keychain, so please use that to store your credentials!!!
If I understood your requirement correctly, You can follow the below guideline.
Before inserting the data to the table, You can check if any record is added to the table.
If the count is 0, you can proceed to insert the record, else you can prompt the error.
I think this will be the simplest way to do this.
First step is to check the database exist in resource folder and create database:-
1)
-(void)checkAndCreateDatabase{
databaseName = #"databasename.sql";
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
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];
}
2) call the function to retrieve the data:-
-(NSMutableArray *) readFromDatabase:(NSString *)query{
// Setup the database object
sqlite3 *database;
// Init the animals Array
returnArray = [[NSMutableArray alloc] init];
NSString *readCommand= query;
// Open the database from the users filessytem
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
// Setup the SQL Statement and compile it for faster access
const char *sqlStatement = [readCommand UTF8String];
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
// Loop through the results and add them to the feeds array
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
// Read the data from the result row
[returnArray addObject:[NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 0)]];
[returnArray addObject:[NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)]];
}
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
return returnArray;
}
3) check for the retrun array is empty or nil then call the insert function defined at step
4) Insert into the database
-(void)insertIntoDatabase:(NSString *)username:(NSString *)password{
// Setup the database object
sqlite3 *database;
// Open the database from the users filessytem
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
// Setup the SQL Statement and compile it for faster access
NSString *insertCommand=#"Insert into tableName values(username,password)";
const char *sqlStatement = [insertCommand UTF8String];
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
// Loop through the results and add them to the feeds array
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {}
sqlite3_finalize(compiledStatement);
}
// Release the compiled statement from memory
}
sqlite3_close(database);
}
You can use NSUserDefaults saving locally it. following code is for saving data.
perf = [NSUserDefaults standardUserDefaults];
[perf setObject:#"1" forKey:#"remember"];
[perf setObject:emailAddresTextField.text forKey:#"email"];
[perf setObject:passwordTextField.text forKey:#"password"];
[perf synchronize];
And for retrieving data use following code
perf=[NSUserDefaults standardUserDefaults];
NSString *remString = [perf stringForKey:#"remember"];
if ([remString isEqualToString:#"1"]) {
emailAddresTextField.text=[perf stringForKey:#"email"];
passwordTextField.text = [perf stringForKey:#"password"];
[rememberBtn setTag:1];
[rememberBtn setImage:[UIImage imageNamed:#"on_radioButton.png"] forState:0];
}
I am inserted first record in sqlite, and when i am trying to add another it shows "Database Locked" error.
the code is:
- (void) addRecord:(NSMutableDictionary *)recordDict
{
if(addStmt == nil) {
const char *sql = "insert into Product(ProID, BarCode , ProductName ) Values(?,?,?)";
if(sqlite3_prepare_v2(database, sql, -1, &addStmt, NULL) != SQLITE_OK)
NSAssert1(0, #"Error while creating add statement. '%s'", sqlite3_errmsg(database));
}
NSInteger recordId = [[recordDict objectForKey:#"ProID"] intValue];
NSLog(#"%d",recordId);
sqlite3_bind_int(addStmt, 1,recordId);
sqlite3_bind_text(addStmt, 2, [[recordDict objectForKey:#"BarCode"] UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(addStmt, 3, [[recordDict objectForKey:#"ProductName"] UTF8String], -1, SQLITE_TRANSIENT);
if(SQLITE_DONE != sqlite3_step(addStmt))
NSAssert1(0, #"Error while inserting data. '%s'", sqlite3_errmsg(database));
else
{//SQLite provides a method to get the last primary key inserted by using sqlite3_last_insert_rowid
rowID = sqlite3_last_insert_rowid(database);
NSLog(#"last inserted rowId = %d",rowID);
//Reset the add statement.
sqlite3_reset(addStmt);
}
}
Nothing in your code jumps out at me. If you are getting "Database Locked" then that means you have an open transaction that is locking the database. This could be in your app (in another thread perhaps) or in another app that is also accessing the same database.
"Database Locked" means the database is not writable, two reasons for this -
1) You forget to finlalize your previous compiled statement or forgot to close the database try this in your previous query -
sqlite3_finalize(compiledStatement);
sqlite3_close(database);
2) Are you copying the database in Documents directory before using it? You can't modify the database in bundle itself. Here is the code to copy the database in documents directory.
-(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:databasePath error:nil];
[fileManager release];
}
Here is how to get database path -
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [[documentsDir stringByAppendingPathComponent:databaseName] retain];
NSLog(#"%#",databasePath);
I have a problem with my app it opens this database and selects rows from it ok,
Then when I want to add new rows using the following code and I always get the following problem at the execution of the prepare_V2.
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Error while creating add statement. 'out of memory''
code is .....
static sqlite3 *database = nil;
static sqlite3_stmt *addStmt = nil;
- (BOOL)addUserprofile {
addStmt = nil; // set to force open for testing
database = nil; // set to force creation of addstmt for testing
if (database == nil) { // first time then open database
NSString *databaseName = #"UserProfile.db";
// Use editable database paths
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
NSLog(#"path = %#",databasePath);
NSLog(#"opening Database");
sqlite3 *database;
// Open the database from the users filessytem
if (sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
NSLog(#"Database Open");
}
else {
NSLog(#"Database did not open");
}
}
if(addStmt == nil) {
NSLog(#"Creating add stmt");
const char *sql = "INSERT INTO Profile (ProfileName) VALUES(?)";
if(sqlite3_prepare_v2(database, sql, -1, &addStmt, NULL) != SQLITE_OK) {
NSAssert1(0, #"** Error while creating add statement. '%s'", sqlite3_errmsg(database));
success = NO;
return success;
}
}
sqlite3_bind_text(addStmt, 1, [ProfileName UTF8String], -1, SQLITE_TRANSIENT);
Sometime, your database is being SQLITE_BUSY or SQLITE_LOCKED.
You can refer this framework to know how to do:
https://github.com/ccgus/fmdb
Good luck!:)