saving json data into sqlite database in iOS - ios

My question is how to insert a fetched JSON array data into sqlite database.
I have fetched JSON data which is an array of dictionaries.
My code to save JSON result looks like this:
-(BOOL) saveApiResults: (NSString *)tableName : (NSArray *)data
{
BOOL saveSuccess = NO;
#try {
const char *dbPath = [databasePath UTF8String];
if(sqlite3_open(dbPath,&database)==SQLITE_OK) {
sqlite3_exec(database, "BEGIN", 0, 0, 0);
//pass an array containing json dictionary to below line
NSDictionary *rowData=[data objectAtIndex:0];
NSArray *keyArray = [rowData allKeys];
NSLog(#"key array %#",keyArray);
NSString *insertSQL=#"INSERT INTO ";
insertSQL=[insertSQL stringByAppendingString:#"moodsdata"];
insertSQL=[insertSQL stringByAppendingString:#" VALUES("];
for(int j=0;j<[keyArray count];j++)
{
insertSQL=[insertSQL stringByAppendingString:#"?"];
if(j<[keyArray count]-1)
insertSQL=[insertSQL stringByAppendingString:#","];
}
insertSQL=[insertSQL stringByAppendingString:#");"];
NSLog(#"query : %# ",insertSQL);
const char *sqlstatement = [insertSQL UTF8String];
sqlite3_stmt *compiledstatement;
if(sqlite3_prepare_v2(database,sqlstatement , -1, &compiledstatement, NULL)==SQLITE_OK) {
for (NSUInteger i = 0; i < [data count]; i++) {
NSDictionary *rowData=[data objectAtIndex:0];
for(int j=0;j<[keyArray count];j++) {
NSString *val = #"";
NSString *value=(NSString *)[rowData objectForKey:[keyArray objectAtIndex:j]];
if((value != nil) && (![value isEqual:[NSNull null]]))
val=[NSString stringWithFormat:#"%#",value];
NSLog(#"values %#",val);
sqlite3_bind_text(compiledstatement,j+1,[val UTF8String], -1, SQLITE_TRANSIENT);
}
if(sqlite3_step(compiledstatement) != SQLITE_DONE) {
NSLog(#"ERROR");
}
sqlite3_clear_bindings(compiledstatement);
sqlite3_reset(compiledstatement);
}
sqlite3_exec(database, "COMMIT", 0, 0, 0);
saveSuccess = YES;
NSLog(#"RESULTS SAVED SUCCESSFULLY!");
} else {
NSLog(#"StatemenT FAILED (%s)", sqlite3_errmsg(database));
}
sqlite3_finalize(compiledstatement);
} else {
NSLog(#"Statement FAILED (%s)", sqlite3_errmsg(database));
}
}
#catch (NSException *exception) {
NSLog(#"NSException : %#",exception.description);
}
#finally {
sqlite3_close(database);
}
return saveSuccess;
}
My question is when I try to pass the JSON array to this method it only saves the value for first array object . i.e. only first dictionary values get saved. Please tell me what I am doing wrong.

You primary issue is that you mistakenly access data[0] instead of data[i] in your loop that does the actual inserting.
But there are lots of other little issues with this code. Here's your code all cleaned up:
-(BOOL) saveApiResults:(NSString *)tableName data:(NSArray *)data
{
BOOL saveSuccess = NO;
const char *dbPath = [databasePath UTF8String];
if (sqlite3_open(dbPath,&database) == SQLITE_OK) {
sqlite3_exec(database, "BEGIN", 0, 0, 0);
//pass an array containing json dictionary to below line
NSDictionary *rowData = data[0];
NSArray *keyArray = [rowData allKeys];
NSLog(#"key array %#",keyArray);
NSMutableString *insertSQL = #"INSERT INTO moods data VALUES(";
for (NSInteger j = 0; j < [keyArray count]; j++)
{
if (j) {
[insertSQL appendString:#","];
}
[insertSQL appendString:#"?"];
}
[insertSQL appendString:#");"];
NSLog(#"query : %# ",insertSQL);
const char *sqlstatement = [insertSQL UTF8String];
sqlite3_stmt *compiledstatement;
if (sqlite3_prepare_v2(database, sqlstatement, -1, &compiledstatement, NULL) == SQLITE_OK) {
for (NSDictionary *rowData in data) {
for (NSInteger j = 0; j < [keyArray count]; j++) {
NSString *val = #"";
NSString *value = rowData[keyArray[j];
if (value && ![value isEqual:[NSNull null]]) {
val = [NSString stringWithFormat:#"%#",value];
}
NSLog(#"values %#",val);
sqlite3_bind_text(compiled statement, j + 1, [val UTF8String], -1, SQLITE_TRANSIENT);
}
if (sqlite3_step(compiledstatement) != SQLITE_DONE) {
NSLog(#"ERROR");
}
sqlite3_reset(compiledstatement);
}
sqlite3_exec(database, "COMMIT", 0, 0, 0);
sqlite3_finalize(compiledstatement);
saveSuccess = YES;
NSLog(#"RESULTS SAVED SUCCESSFULLY!");
} else {
NSLog(#"StatemenTtFAILED (%s)", sqlite3_errmsg(database));
}
sqlite3_close(database);
} else {
NSLog(#"Statement FAILED (%s)", sqlite3_errmsg(database));
}
return saveSuccess;
}
There is no need for try/catch. Just do proper error checking.
Use NSMutableString to build up a string piece by piece.
Only finalize a statement if you successfully prepare it.
Be sure to close a database if you open it.
Use modern syntax for accessing values from dictionaries and arrays. It's easier to read and type.
Use modern loops when possible.
Use whitespace. It makes the code easier to read.
Give all of your method parameters a name.
But your SQLite code was actually better than most posts here. You actually use sqlite_bind_xxx to bind values to your query. Too few people do that.

In your code where you're grabbing the values, you are grabbing the row data with:
NSDictionary *rowData=[data objectAtIndex:0];
I suspect you mean:
NSDictionary *rowData=[data objectAtIndex:i];

Related

Takes more time in insertion of images in the database, what to do?

I am new in ios, in my product cataloge app I am getting image links from json and after parsing it i am converted it into nsdata and inserting it into sqlite,
problem is that it takes more time than android app for inserting image into database, and i also have a doubt it takes longer than normal time to inserting is there any wrong with my code, please help.
this is the first method to parse images and orther text data
-(void)jsonparseimage:(NSString *)jsonstring
{
NSError *error;
SBJSON *json = [SBJSON new];
NSDictionary *statusDict=[json objectWithString:jsonstring error:&error ];
NSArray *feed= [statusDict objectForKey:#"CATEGORY_DETAILS"];
if([[statusDict objectForKey:#"CATEGORY_DETAILS" ]count ]>0)
{
[appdelegate.categoryarray removeAllObjects];
for (NSDictionary *dictionaryValue in feed)
{
for (NSString *valueKey in [dictionaryValue allKeys])
{
CategoryData *catData=[[CategoryData alloc]init];
catData.categoryname=valueKey;
[appdelegate.categoryarray addObject:valueKey];
NSArray *catInfo =[dictionaryValue objectForKey:valueKey];
NSMutableArray *newArray=[[NSMutableArray alloc]init];
for (NSMutableDictionary *categoryData in catInfo)
{
imageconvert *convert=[[imageconvert alloc]init];
convert.thumbimage=[categoryData objectForKey:#"thumb"];
convert.mainimage=[categoryData objectForKey:#"mainimg"];
convert.categaryname=[categoryData objectForKey:#"cat_nm"];
convert.imageName=[categoryData objectForKey:#"image_nm"];
convert.lastmodified=[categoryData objectForKey:#"lastmodified"];
[newArray addObject:convert];
}
catData.imageArraya=newArray;
NSLog(#"all Dataaaaa :%d %d %#",[catData.imageArraya count],[alldata count],catData.categoryname);
[alldata addObject:catData];
}
}
}
[self.tableView reloadData];
[self insertintoDatabase:alldata];
}
this is second to fetch imagedata in nsdata from link
-(void)insertintoDatabase:(NSMutableArray *)allCatData
{
for (int i=0;i<[allCatData count] ;i++)
{
CategoryData *catData=[allCatData objectAtIndex:i];
appdelegate.categoryString=catData.categoryname;
NSLog(#"array size :%d",[catData.imageArraya count]);
for (int j=0; j<[catData.imageArraya count]; j++)
{
imageconvert *convertedData=[catData.imageArraya objectAtIndex:j];
NSError* error = nil;
NSData* thumbData = [NSData dataWithContentsOfURL: [NSURL URLWithString:[convertedData.thumbimage stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]] options:NSDataReadingUncached error:&error];
if (error)
{
NSLog(#"%#", [error localizedDescription]);
}
else
{
[self SaveThumbDatatosql:thumbData WithCatName:catData.categoryname withImagename:convertedData.imageName withLastmodified:convertedData.lastmodified];
}
}
}
[blurView removeFromSuperview];
[self viewDidLoad];
}
this is method for insertion
- (void) SaveThumbDatatosql:(NSData*) thumbData WithCatName:(NSString*) CategoryName withImagename:(NSString*) ImageName withLastmodified:(NSString*) Lastmodified
{
// NSLog(#"thumbData:%# imageData:%# CategoryName:%# ImageName:%# Lastmodified:%#",thumbData,imageData,CategoryName,ImageName,Lastmodified);
sqlite3 *database;
if (sqlite3_open([appdelegate.databasePath UTF8String], &database) == SQLITE_OK)
{
sqlite3_stmt *statement;
const char* sqliteQuery = "INSERT INTO CategoryTable (ThumbImagePath,Categoryname,Imagename,Lastmodified) VALUES (?,?,?,?)";
if (sqlite3_prepare_v2(database, sqliteQuery, -1, &statement, NULL) == SQLITE_OK)
{
sqlite3_bind_blob(statement, 1, [thumbData bytes], [thumbData length], SQLITE_STATIC);
sqlite3_bind_text(statement, 2, [CategoryName UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statement, 3, [ImageName UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statement, 4, [Lastmodified UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_step(statement);
sqlite3_finalize(statement);
NSLog(#"inserted ");
}
else
{
NSAssert1(0, #"error:'%s'", sqlite3_errmsg(database));
// NSLog(#"not inserted ");
}
sqlite3_close(database);
}
}

how to fetch data from sqlite into an array

This is my code.
It is only returning the first row from database. I know something is really wrong but don't know what. Please help.
if(sqlite3_open([databasePath UTF8String],&texttalkdb)==SQLITE_OK)
{
sqlite3_stmt *statement;
if(sqlite3_prepare_v2(texttalkdb, [sql UTF8String], -1,&statement, NULL)==SQLITE_OK)
{
if(sqlite3_step(statement)==SQLITE_ROW)
{
for(int i=0;i<=20;i++)
{
char *pass=(char*)sqlite3_column_text(statement,i);
NSString *passStr = [NSString stringWithFormat:#"%s",pass];
NSString *msg=[[NSString alloc]initWithUTF8String:pass];
[arr addObject:msg];
}
}
sqlite3_finalize(statement);
}
sqlite3_close(texttalkdb);
}
NSLog(#"%#",arr);
return arr;
Try this
if(sqlite3_prepare_v2(texttalkdb, [sql UTF8String], -1,&statement, NULL)==SQLITE_OK)
{
while( sqlite3_step(statement) == SQLITE_ROW )
{
//get records
}
}
Step 1: This is for check the getting values is empty or not
+(NSString*)charToString:(char*)chart
{
NSString *string = #" ";
if(string)
{
chart = [self checkEmptyChar:chart];
string=[NSString stringWithUTF8String:chart];
}
return string;
}
Step 2: This is for checking character
+(char *)checkEmptyChar:(char *)check
{
NSString *string = #" ";
if (check == NULL)
check = string;
return check;
}
Step 3: To fetch SQLite Coding
if (sqlite3_prepare_v2(database, [query UTF8String], -1, &stment, nil) == SQLITE_OK)
{
while (sqlite3_step(stment) == SQLITE_ROW)
{
for(int i=0;i<=20;i++)
{
NSString *msg = [self charToString: (char *)sqlite3_column_text(stment, i)];
[array addObject:msg];
}
}
sqlite3_reset(stment);
}
sqlite3_finalize(stment);
}
sqlite3_close(database);

Why does one statement work and one not

I have a table in a database which I access like this:
+(NSMutableArray *) queryDatabaseWithSQL:(NSString *)sql params:(NSArray *)params {
sqlite3 *database;
NSMutableArray *rtn = [[NSMutableArray alloc] init];
int index = 0;
NSString *filepath = [self copyDatabaseToDocumentsWithName:#"database"];
if (sqlite3_open([filepath UTF8String], &database) == SQLITE_OK) {
const char *sqlStatement = [sql cStringUsingEncoding:NSASCIIStringEncoding];
sqlite3_stmt *compiledStatement;
if (sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
if ([params count]) {
for (int i = 0; i < [params count]; i++) {
NSString *obj = [params objectAtIndex:i];
sqlite3_bind_blob(compiledStatement, i + 1, [obj UTF8String], -1, SQLITE_TRANSIENT);
}
}
while (sqlite3_step(compiledStatement) == SQLITE_ROW) {
NSMutableArray *arr = [[NSMutableArray alloc] init];
index = 0;
const char *s = (char *)sqlite3_column_text(compiledStatement, index);
while (s) {
[arr addObject:[NSString stringWithUTF8String:s]];
index++;
s = (char *)sqlite3_column_text(compiledStatement, index);
}
if (![rtn containsObject:arr]) {
[rtn addObject:arr];
}
}
}
sqlite3_finalize(compiledStatement);
}
NSLog(#"ERROR: %s", sqlite3_errmsg(database));
sqlite3_close(database);
return rtn;
}
This works fine when I call the function like this:
NSLog(#"%#", [database queryDatabaseWithSQL:#"SELECT * FROM FRIENDSUSERS WHERE USER = ?" params:#[[delegate->user objectForKey:#"username"]]]);
Then when I call the function using a string like this it doesn't return any rows:
NSLog(#"%#", [database queryDatabaseWithSQL:#"SELECT * FROM FRIENDSUSERS WHERE USER = ?" params:#[#"username"]]);
I haven't got a clue what is going one with but I have checked the strings match and they do so I'm now stuck
Can anyone see any reason why this would not work
I have run error checks as well and every time it returns no error, or rows :(
Thanks in advance

what the issue with my update function ? that updated perfectly after sqlite databse locked error?

- (void) updateTimeStamp:(NSDictionary *) record forRowID: (NSString *) updateTableName
{
int dictionarySize = [record count];
NSMutableData *dKeys = [NSMutableData dataWithLength:sizeof(id) *dictionarySize];
NSMutableData * dValues = [NSMutableData dataWithLength: sizeof(id) * dictionarySize];
[record getObjects:(__unsafe_unretained id *)dValues.mutableBytes andKeys:(__unsafe_unretained id *)dKeys.mutableBytes];
[dValues appendBytes:&updateTableName length:sizeof(id)];
NSString * query = [NSString stringWithFormat:#"update %# set %# = ? where Table_Name= ?",tableName,[[record allKeys] componentsJoinedByString:#" = ?, "]];
[self bindSQL:[query UTF8String] withVargs:(va_list)dValues.mutableBytes];
if(sqlite3_step(statment) == SQLITE_DONE);
{
NSLog(#"prepead Timestamp %s",sqlite3_errmsg(database));
}
sqlite3_reset(statment);
#synchronized(self)
{
if(sqlite3_finalize(statment) != SQLITE_OK)
{
NSLog(#"doQuery: sqlite3_finalize failed (%s)", sqlite3_errmsg(database));
}
}
}
When i run application that function work perfectly? not an any issue but that function after database is locked ? what the problem with the function please give me solution
i have many table so i open database once time in function when all timestamp updated after close the database.
bindSQL function below
- (void) bindSQL:(const char *) cQuery withVargs:(va_list)vargs
{
int param_count;
NSLog(#"%d",sqlite3_prepare_v2(database, cQuery, -1, &statment, NULL));
if(sqlite3_prepare_v2(database, cQuery, -1, &statment, NULL) != SQLITE_OK)
{
NSLog(#"bindSQL: could not prepare statement (%s) %s", sqlite3_errmsg(database), cQuery);
statment = NULL;
return;
}
if((param_count = sqlite3_bind_parameter_count(statment)))
{
for(int i =0; i < param_count ;i++)
{
id object = va_arg(vargs,id);
if(object == nil)
{
sqlite3_bind_null(statment, i+1);
}
else if ([object respondsToSelector:#selector(objCType)])
{
if(strchr("islqISLBQ", *[object objCType]))
{
sqlite3_bind_int(statment, i+1, [object intValue]);
}
else if (strchr("fd", *[object objCType]))
{
sqlite3_bind_double(statment, i+1, [object doubleValue]);
}
else
{
NSLog(#"bindSQL: Unhandled objCType: %s query: %s", [object objCType], cQuery);
statment = NULL;
return;
}
}
else if ([object respondsToSelector:#selector(UTF8String)])
{
sqlite3_bind_text(statment, i+1, [object UTF8String], -1, SQLITE_TRANSIENT);
}
else if ([object isEqual:[NSNull null]])
{
statment = NULL;
return;
}
else
{
NSLog(#"bindSQL: Unhandled parameter type: %# query: %s", [object class], cQuery);
statment = NULL;
return;
}
}
}
va_end(vargs);
return;
}
This code creates two statement objects:
NSLog(#"%d",sqlite3_prepare_v2(database, cQuery, -1, &statment, NULL));
if(sqlite3_prepare_v2(database, cQuery, -1, &statment, NULL) != SQLITE_OK)
of which only one is finalized later.
If you want to check the return code, better use a variable:
int rc = sqlite3_prepare_v2(database, cQuery, -1, &statment, NULL);
NSLog(#"%d", rc);
if (rc != SQLITE_OK)

Populating array from select query in iOS

So I'm fairly new to iOS development and I'm having problems with my select function. I made a function that should take in a select query and the table name and return an array of results where each array entry is a dictionary with a row of results. Somehow my query for column names is deleting my columnNames variable and returning crazy results. I'm just trying to figure out an easy way to store, access, manipulate query results
Here is the function that converts results into array:
-(NSMutableArray *)selectQuery:(NSString*)query
table:(NSString*)table
{
NSMutableArray *returnArray = [NSMutableArray new];
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
NSMutableArray *columnNames;
NSString *tableQuery = [NSString stringWithFormat:#"PRAGMA table_info('%#')", table];
if (sqlite3_prepare_v2(database, [tableQuery UTF8String], -1, &statement, nil) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW)
{
[columnNames addObject:[NSString stringWithUTF8String:(const char*)sqlite3_column_text(statement, 1)]];
}
}
else
{
NSLog(#"Error preparing table query:");
NSLog(tableQuery);
}
if (sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, nil) == SQLITE_OK)
{
while(sqlite3_step(statement)==SQLITE_ROW)
{
NSMutableDictionary *temp= [NSMutableDictionary new];
for (int i = 0; i < [columnNames count]; i++)
{
[temp setObject:[NSString stringWithUTF8String:(const char*)sqlite3_column_text(statement, i)] forKey:columnNames[i]];
}
if (temp != nil)
{
[returnArray addObject:temp];
temp = nil;
}
}
sqlite3_reset(statement);
sqlite3_close(database);
}
else
{
NSLog(#"Error preparing select statement with query:");
NSLog(query);
}
}
else
{
NSLog(#"Could not open database");
}
return returnArray;
}
and heres the call to it
NSMutableArray *queryResults = [dbInstance selectQuery:[NSString stringWithFormat:#"SELECT gallons, mileage FROM fillups WHERE carId = \"%d\" ORDER BY date asc",
carId]
table:#"fillups"];
You are never instantiating columnNames. Thus, your attempt to add column names to that array will not succeed. To remedy this, when you declare it, you want to instantiate the mutable array object, too:
NSMutableArray *columnNames = [NSMutableArray array];
Unrelated to this problem, when you're done retrieving the column names, before you prepare your second statement, don't forget to release the memory associated with the first prepared statement:
sqlite3_finalize(statement);
Finally, when you're done retrieving the second prepared SQL statement, rather than calling sqlite3_reset, you want to call sqlite3_finalize again for that second prepared statement. The sqlite3_reset is used to reset a statement when you want to bind new values to ? placeholders in a statement, which is not applicable here, so no sqlite3_reset is needed. But if you don't call sqlite3_finalize, you're not releasing the memory associated with the prepared statement.
By the way, if you wanted to dynamically retrieve the column names and column types (without having to do PRAGMA table_info), you could do something like:
int rc;
if ((rc = sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, NULL)) != SQLITE_OK) {
NSLog(#"select failed %d: %s", rc, sqlite3_errmsg(database));
}
NSMutableArray *returnArray = [NSMutableArray array];
NSInteger columnCount = sqlite3_column_count(statement);
id value;
while ((rc = sqlite3_step(statement)) == SQLITE_ROW) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
for (NSInteger i = 0; i < columnCount; i++) {
NSString *columnName = [NSString stringWithUTF8String:sqlite3_column_name(statement, i)];
switch (sqlite3_column_type(statement, i)) {
case SQLITE_NULL:
value = [NSNull null];
break;
case SQLITE_TEXT:
value = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(statement, i)];
break;
case SQLITE_INTEGER:
value = #(sqlite3_column_int64(statement, i));
break;
case SQLITE_FLOAT:
value = #(sqlite3_column_double(statement, i));
break;
case SQLITE_BLOB:
{
NSInteger length = sqlite3_column_bytes(statement, i);
const void *bytes = sqlite3_column_blob(statement, i);
value = [NSData dataWithBytes:bytes length:length];
break;
}
default:
NSLog(#"unknown column type");
value = [NSNull null];
break;
}
dictionary[columnName] = value;
}
[returnArray addObject:dictionary];
}
if (rc != SQLITE_DONE) {
NSLog(#"error returning results %d %s", rc, sqlite3_errmsg(database));
}
sqlite3_finalize(statement);

Resources