App crashes when trying to load from SQLite - ios

Well, I am making an update to an app I made, but I am having some runtime issues. People said they wanted to be able to save data they entered, so I am implementing an SQLite database. I got the app to check if database exists and create everything needed, if the database or database table does not exist. However, I am having troubles retrieving data from SQLite. When I press a button that should load data, the app crashes. I tried using both NSLog and UIAlertView to figure out what is going on, but I cannot retrieve results. However, when performing a test run in SQLite, the query I am issuing is correct, so there must be something else.
the following is the code I am using to retrieve data:
- (IBAction)loadData
{
NSDateFormatter* bf = [[NSDateFormatter alloc] init];
[bf setDateFormat:#"MM/dd/yyyy"];
NSDate* sdate;
NSString* birthDate;
NSString* balance;
NSString* dyear;
sqlite3_stmt *stmt;
// get document directory
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [dirPaths objectAtIndex:0];
// build path to database
dbpath = [[NSString alloc] initWithString:[docsDir stringByAppendingPathComponent:#"mrd.db"]];
const char *dpath = [dbpath UTF8String];
if (sqlite3_open(dpath, &mrdDB) == SQLITE_OK)
{
NSString* query = [NSString stringWithFormat:#"SELECT date(birth), bal, year FROM rmd LIMIT 1"];
const char* query_statement = [query UTF8String];
if (sqlite3_prepare_v2(mrdDB, query_statement, -1, &stmt, NULL) == SQLITE_OK)
{
if (sqlite3_step(stmt) == SQLITE_ROW)
{
birthDate = [[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(stmt, 1)];
balance = [[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(stmt, 2)];
dyear = [[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(stmt, 3)];
sdate = [bf dateFromString:birthDate];
self.birth.text = [bf stringFromDate:sdate];
[birthDate release];
[bf release];
self.bal.text = balance;
[balance release];
self.year.text = dyear;
[dyear release];
self.status.text = #"data loaded sucessfully";
} else {
self.status.text = #"Cannot find/retrieve saved data";
}
sqlite3_finalize(stmt);
}
sqlite3_close(mrdDB);
}
}
The app is able to save data to the database, as the app displays a "save successful" message, which I added for the user's sake as well as seeing if things were done right, so I am not too sure what is going on.

I found my problem, after finding where the simulator saves databases. My problem was not with the loading the data, but how it was saved. Thanks for the help.

Related

How to get/delete multiple rows in SQLITE table in iOS?

My table in SQLITE is in the following format.
id name date height
1 tt 12-2-15 120
2 ss 15-3-15 110
3 tt 14-5-15 120
....
10 tt 19-6-15 130
(1)I like to get all rows under name tt in one instruction, then put into two dimensional array. How can i implement it?
(2)How to delete all rows under name tt in one instruction?
My code is in iOS. I couldn't find solution easily.
EDIT:
Just to share what I implement.
NSString *databasePath;
NSString *docsDir;
NSArray *dirPaths;
//Database
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
// Build the path to the database file
databasePath = [[NSString alloc]initWithString:[docsDir stringByAppendingPathComponent: #"RECORDS.db"]];
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath,& RECORDS) == SQLITE_OK)
{
retval = [[NSMutableArray alloc] init];
NSString *query = [NSString stringWithFormat:#"SELECT id, nameid, date, height FROM RECORD WHERE nameid=\"%#\"", currentNameID];
const char *query_stmt = [query UTF8String];
if (sqlite3_prepare_v2(RECORDS, query_stmt, -1, &statement, nil) == SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW) {
char *date = (char *) sqlite3_column_text(statement, 1);
int height = (int) sqlite3_column_text(statement, 2);
NSString *name = [[NSString alloc] initWithUTF8String:date];
HeightInfo *info = [[HeightInfo alloc] initWithInfos:name withheight:(int)height];
[retval addObject:info];
}
sqlite3_finalize(statement);
sqlite3_close(RECORDS);
}
}
Thanks
Do you need help with the SQL or the ios framework?
The SQL is pretty straightforward;
select id, name, date, height from mytablename where name = "tt";
delete from mytablename where name == "tt";
Practice the commands on the command line so you see how they work.
For processing your data look at the great examples by Ray Wenderlich;
http://www.raywenderlich.com/913/sqlite-tutorial-for-ios-making-our-app

cannot fetch value from sqlite in IOS

Table values:
ID=1 CUSTOMERID=1 NAME=John EMAIL=email USERNAME=usernaeme
I am using this code to fetch customerId from Usertable with this code
#try {
//CustomerIdField=#"admin";
// customerUsername=#"admin";
NSLog(#"the value of customerusername is %#",customerUsername);
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [dirPaths objectAtIndex:0];
NSLog(#"inside function");
databasePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent: #"EBook.db" ]];
const char *dbpath;
#try {
dbpath = [databasePath UTF8String];
NSLog(#"the const char is %s",dbpath);
}
#catch (NSException *exception) {
NSLog(#"the exception3 is %#",exception);
}
if (sqlite3_open(dbpath, &Ebookreaderdb) == SQLITE_OK)
{
NSString *selectSQL = [NSString stringWithFormat: #"SELECT CUSTOMERID FROM Usertable WHERE EMAIL=\"%#\"",customerUsername];
sqlite3_stmt *selstatement;
const char *select_stmt = [selectSQL UTF8String];
//NSMutableArray *resultArray = [[NSMutableArray alloc]init];
if (sqlite3_prepare_v2(Ebookreaderdb,
select_stmt, -1, &selstatement, NULL ) == SQLITE_OK)
{
NSLog(#"inside sqlite OK"); //this prints in log
if (sqlite3_step(selstatement) == SQLITE_ROW)
{
NSLog(#"inside sqlite ROW"); // this is also printing in log
NSString *userInfoStr = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selstatement,1)];
NSLog(#"val is %#",userInfoStr);
char *tmp = sqlite3_column_text(selstatement,1);
if (tmp == NULL)
CustomerIdField = nil;
else
CustomerIdField = [[NSString alloc] initWithUTF8String:tmp];
CustomerIdField = [[NSString alloc] initWithUTF8String:
(const char *) sqlite3_column_text(selstatement,1)];
NSLog(#"inside customer is %#",CustomerIdField);
// [resultArray addObject:name];
}
else{
NSLog(#"Not found");
// return nil;
}
sqlite3_reset(selstatement);
}
But i am getting this exception Newpjtonfriday[2165:84017] the exception is 2 *** +[NSString stringWithUTF8String:]: NULL cString
I googled with the above result and everywhere it is saying that the value is null that is why the exception occurs. but in my case the value is there.
Because the code
NSLog(#"inside sqlite ROW");
is coming in log meaning that a row exists in table. But cannot fetch it.
Please help
I think it's a typo:
#"SELECT CUSTOMERID FROM Usertable WHERE EMAIL=\"%#\"",customerUsername;
You are passing the username instead of the e-mail? Or maybe you meant to SELECT ... WHERE USERNAME=?
Also there is no need for any of those #try/#catch blocks as I cannot see how an Objective-C exception can be thrown by that code.
One last thing; in order to avoid SQL Injection attacks you should bind the values into your statements, rather than formatting them as a string, as you have done here.
Little advise if you are just starting to develop on iOS, try to use some library(FMDB to easy work with SQLite) to make some task more straight forward.
Answer to your Question
Try the "SELECT * FROM Usertable" if it has any row in your table.
Second you should check if the value in your row is not NULL.
To get easy solution use something like Datum SQLite Free(Mac os X) OR you could use SELECT COUNT to ensure you have any rows in table.
Try your select and if the app return the rows but there is no value in your column that's mean you have problems with write to DB logic but not in read logic and you are trying to fetch row with have null value on column you want.That's why this method is not working:
[NSString stringWithUTF8String:]: NULL cString
its thrown an exception because you have no string and trying to send NULL to method.
The sqlite3_column_text index number is zero-based, not one-based. Thus, this line:
char *tmp = sqlite3_column_text(selstatement, 1);
Should be:
char *tmp = sqlite3_column_text(selstatement, 0);
By the way, your handling of this tmp variable is a prudent way of checking to make sure it's not NULL before you use it. Unfortunately, elsewhere in this same routine you use sqlite3_column_text value directly (which is why your app crashed, rather than gracefully reporting the error). You have several redundant calls to sqlite3_column_text here. I would suggest employing the pattern you used with this tmp variable.
Let me give you my suggestion I tried what you just did and I also got the error. Then I did some googling and found this link
http://www.raywenderlich.com/913/sqlite-tutorial-for-ios-making-our-app
just modify you codes from these
char *tmp = sqlite3_column_text(selstatement,1);
if (tmp == NULL)
CustomerIdField = nil;
else
CustomerIdField = [[NSString alloc] initWithUTF8String:tmp];
CustomerIdField = [[NSString alloc] initWithUTF8String:
(const char *) sqlite3_column_text(selstatement,1)];
NSLog(#"inside customer is %#",CustomerIdField);
to
int tmp = sqlite3_column_int(selstatement,0);// my case it is int
char *nameChars = (char *) sqlite3_column_text(selstatement, 1);// here is the change occuring please refer this
NSString *name = [[NSString alloc] initWithUTF8String:nameChars]; // there are two steps first fetch as char, then change to String
NSLog(#"customerid is %d",tmp);
NSLog(#"customer name is %#",name);
Try this, It worked for me..
I think the problem is that you are fetching the coustomerId as String directly
In you case just try this
char *custId = (char *) sqlite3_column_text(selstatement, 0);
NSString *customerId = [[NSString alloc] initWithUTF8String:custId];
:-)

Data is not fetched from sqlite database in iphone device but working in iOS Simulator

In my app I want to display data from sqlite database into UITableView.It is working fine in ios simulator but data is not fetched when using iphone device.I dont understand what the issue is.I have added Db.sqlite file into my project.Plz help me in solving this.My code is below:
+(BOOL)copyFile{
NSString *strSourcePath=[[NSBundle mainBundle]pathForResource:#"Db" ofType:#"sqlite"];
NSString *strHome=NSHomeDirectory();
NSString *strDestPath=[NSString stringWithFormat:#"%#/Documents/NewDb.sqlite",strHome];
NSFileManager *manager=[NSFileManager defaultManager];
NSError *err;
if ([manager fileExistsAtPath:strDestPath]==NO) {
[manager copyItemAtPath:strSourcePath toPath:strDestPath error:&err];
return YES;
}
else{
NSLog(#"file already exists");
return NO;
}
return YES;
}
+(NSMutableArray*)getDetails{
NSString *strHome = NSHomeDirectory();
NSString *strDestPath = [NSString stringWithFormat:#"%#/Documents/NewDb.sqlite",strHome];
NSMutableArray *arrDetails=[[NSMutableArray alloc]init];
sqlite3 *db;
int n = sqlite3_open([strDestPath UTF8String], &db);
if (n==SQLITE_OK) {
NSString *strQuery=#"SELECT * FROM Categories";
sqlite3_stmt *stmt;
char *err_msg;
int res=sqlite3_prepare_v2(db, [strQuery UTF8String], -1, &stmt, &err_msg);
if (res == SQLITE_OK) {
while (sqlite3_step(stmt)==SQLITE_ROW) {
Categories *categories=[[Categories alloc]init];
categories.categoryId=sqlite3_column_int(stmt,0);
categories.categoryName=[NSString stringWithUTF8String:(char *) sqlite3_column_text(stmt, 1)];
categories.categoryImage=[NSString stringWithUTF8String:(char *) sqlite3_column_text(stmt, 2)];
[arrDetails addObject:categories];
}
}
}
return arrDetails;
}
I'm not sure, but it might be the problem what is happening to images displayed with uppercase letters, try changing your sqlite file into lowercase letters. like
NSString *strDestPath = [NSString stringWithFormat:#"%#/Documents/newdb.sqlite",strHome];
Changes are as follows
+(BOOL)copyFile{
//This needs to change
NSString *strDestPath = [YourClassName dbPath];
}
+(NSMutableArray*)getDetails{
//This needs to change
NSString *strDestPath = [YourClassName dbPath];
}
+(NSString *)dbPath{
return [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject] stringByAppendingString:#"NewDb.sqlite"];
}
I would try changing Db by db
NSString *strSourcePath=[[NSBundle mainBundle]pathForResource:#"Db" ofType:#"sqlite"];

iOS csv to sqlite

I am a newbie and working on code below. As a result of creating NSMutableDictionary from CSV file I am passing the value to DatabaseManager class and there is a method which receives the values and trying to insert it to the database.
1. How can I insert those values to DB?
2. Is is efficient way to make little dictionary Application on iOS? (I have approx 40000 records and size is about 3MB)
MAIN CLASS
...
NSString *filePath= [[NSBundle mainBundle] pathForResource:#"Test" ofType:#"csv"];
NSString *content = [[NSString alloc]initWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
//NSString *path1=[NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"JapMon" ofType:#"csv"] encoding:NSUTF8StringEncoding error:nil];
NSArray *messArr=[content componentsSeparatedByString:#"\n"];
if(messArr)
{
NSLog(#"%d", [messArr count]);
for(int i=1;i<=[messArr count]-2;i++)
{
NSMutableDictionary *d=[[NSMutableDictionary alloc] init];
NSString *StrValue=[NSString stringWithFormat:#"%#",[messArr objectAtIndex:i]];
StrValue=[StrValue stringByReplacingOccurrencesOfString:#"\"" withString:#""];
StrValue=[StrValue stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
// Here give whatever saperator you need to saperate data
NSArray *arr=[StrValue componentsSeparatedByString:#","];
//NSLog(#"%#", [arr objectAtIndex:2]);
[d setValue:[arr objectAtIndex:0] forKey:#"word"];
[d setValue:[arr objectAtIndex:1] forKey:#"hansa"];
[d setValue:[arr objectAtIndex:2] forKey:#"def"];
//Here add logic to insert row in your database table
[[DatabaseManager getSharedInstance]insertInitialDataToDb:d];
//NSLog(#"%#", d);
//Add this dictionary "d" into database
[d release]; //Cleanup.
}
}
//[content release];
...
2.DATABASEMANAGER CLASS
-(BOOL) insertInitialDataToDb:(NSMutableDictionary*)initialData
{
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &database) == SQLITE_OK)
{
NSString *insertSQL = [NSString stringWithFormat:#"insert into japmon (word, hansa, def) values (\"%#\")", initialData];
NSLog(#"ok");
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(database, insert_stmt,-1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
return TRUE;
}
else {
return FALSE;
}
sqlite3_reset(statement);
}
return TRUE;
From your query you would like to insert values to three column. But you are passing only dictionary.
NSString *insertSQL = [NSString stringWithFormat:#"insert into japmon (word, hansa, def) values (\"%#\")", initialData];
you need to change you stringWithFormat with this
NSString *insertSQL = [NSString stringWithFormat:#"insert into japmon (word, hansa, def) values (\"%#\",\"%#\",\"%#\"), WORD_COL_VALUE,HANSA_COL_VALUE,DEF_COM_VALUE];
Finally I solved my question on my own. The best way was prepared FTS sqlite database not importing after creation of "core data" database. As a result my app autocomplete/suggestion performance is at most 3.2MB CPU usage # more than 50.000 records in UTF-8 format on simulator like a windspeed.

Fetch Image From Sqllite Database in iphone

I Want to save the image in Database and fetch the image in database.i am convert the image in NSData and the image is store successfully in database but when i fetch that image from database then it crash.it give this warning
Warning :- -[__NSCFString bytes]: unrecognized selector sent to instance 0x7916000
Below code For save the image in Database
NSData *dataImage1 = UIImagePNGRepresentation(image3.image);
NSString *str = [NSString stringWithFormat:#"Insert into Gallery(ImageName) VALUES ('%#')",dataImage1];
[Database executeQuery:str];
//Database.m
+(NSString* )getDatabasePath{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"Database"];
return writableDBPath;
}
+(NSMutableArray *)executeQuery:(NSString*)str{
sqlite3_stmt *statement= nil;
sqlite3 *database;
NSString *strPath = [self getDatabasePath];
NSMutableArray *allDataArray = [[NSMutableArray alloc] init];
if (sqlite3_open([strPath UTF8String],&database) == SQLITE_OK) {
if (sqlite3_prepare_v2(database, [str UTF8String], -1, &statement, NULL) == SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW) {
NSInteger i = 0;
NSInteger iColumnCount = sqlite3_column_count(statement);
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
while (i< iColumnCount) {
NSString *str = [self encodedString:(const unsigned char*)sqlite3_column_text(statement, i)];
NSString *strFieldName = [self encodedString:(const unsigned char*)sqlite3_column_name(statement, i)];
[dict setObject:str forKey:strFieldName];
i++;
}
[allDataArray addObject:dict];
[dict release];
}
}
sqlite3_finalize(statement);
}
sqlite3_close(database);
return allDataArray;
}
//Fetch image
NSString *str =[NSString stringWithFormat:#"Select * from Gallery where id = 1"];
NSMutableArray *arra =[Database executeQuery:str];
NSData *d =[[arra objectAtIndex:0] valueForKey:#"ImageName"];
UIImage *img = [[UIImage alloc] initWithData:d];
NSLog(#"%#",img);
Thanks in Advance
Your problem is that you cannot insert binary data by building a SQL statement with a stringWithFormat. You're inserting the NSData (binary data) into a field called ImageName, and you cannot use stringWithFormat to build that SQL statement.
Did you really mean to insert the image itself? In that case you'd use SQL like below, with a ? placeholder, and then use sqlite3_bind_blob for column number 1 with your NSData before you call sqlite3_step. Thus, the SQL would look like:
Insert into Gallery(ImageName) VALUES (?)
(As an aside, I'm not a fan of the choice of ImageName for the actual data of the image. I'd either use Image for the actual image data, or ImageName for the name of the image.)
And then, after preparing your SQL, you'd then call:
sqlite3_bind_blob(statement, 1, [dataImage1 bytes], [dataImage1 length], SQLITE_TRANSIENT);
You can then invoke sqlite3_step, like normal, to execute the SQL to insert the data.
When retrieving the data, after you successfully sqlite3_step, you'd use the appropriate sqlite3_column_blob functions:
const void *bytes = sqlite3_column_blob(statement, 0);
int len = sqlite3_column_bytes(statement, 0);
NSData *data = [NSData dataWithBytes:bytes length:len];
UIImage *image = [UIImage imageWithData:data];
Having said that, SQLite is notoriously inefficient in storing large blob data. You might want to store the image in the Documents folder and then just store the filename in your ImageName field.
i think you are getting bytes in string type in
[[arra objectAtIndex:0] valueForKey:#"ImageName"];
so first get string and then convert it to data.
try like this:
NSData* data = [[[arra objectAtIndex:0] valueForKey:#"ImageName"] dataUsingEncoding:NSUTF8StringEncoding];

Resources