Saving NSArray in SQLite and retrieving it - ios

In my iPHone app I am saving an NSArray in sqlite like this.
-(void)saveData
NSData *dataFromArray = [NSKeyedArchiver archivedDataWithRootObject:MyArray];
sqlite3_stmt *statement;
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
NSString *insertSQL = [NSString stringWithFormat: #"INSERT INTO ARRAY(PROFILE) VALUES (\"%#\")",dataFromArray];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(contactDB, insert_stmt, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
NSLog(#"Saved Succesfully");
}
else {
// NSAssert1(0, #"Error while inserting data. '%s'", sqlite3_errmsg(contactDB));
NSLog(#"Not saved %d",sqlite3_finalize(statement));
}
sqlite3_finalize(statement);
sqlite3_close(contactDB);
}
}
Data is saved successfully.
Now I tried to fetch the saved data like this-
-(void)fetchData{
const char *dbpath = [databasePath UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
NSString *querySQL = [NSString stringWithFormat: #"SELECT * FROM ARRAY"];
const char *query_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(contactDB, query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
while(sqlite3_step(statement) == SQLITE_ROW)
{
NSString *MYstring = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 0)];
NSData* data = [MYstring dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"data---%#",data);
NSArray *myArrayFromDB = [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
sqlite3_finalize(statement);
}
sqlite3_close(contactDB);
}
}
I am getting print value for "MYstring" like this
<62706c69 73743030 d4000100 02000300 04000500 0806cb06 cc542474 6f705824 6f626a65 63747358 24766572 73696f6e 59246172 63686976 6572d100 06000754 726f6f74 8001af11 022a0009 000a0017 00180019 001a0080 00810082 00830084 00850086 00870088 0089008a 008b008c 008d008e 008f0090 00910092 00930094 00950096 00970098 0099009a 009b009c 009d009e 009f00a0 00a100a2 00a300a4 00a500a6 00a700a8 00a900aa 00ab00ac 00ad00ae 00af00b0 00b100b5 00bd00c0 00c300c6 00c900cc 00cf00d2 00d500d8 00db00de 00e100e4 00e700ea 00ed00f0 00fd0105 0108010b 010e0111 01140117 011a011d 01200122 0127012a 012d0130 01330136 0139013c 013f0142 01450148 014b014e 01510154 0159015e 01610164 0169016f 018101a1 01a201a3 01a401a5 01a601a7 01a801a9 01aa01ab 01ac01ad 01ae01af 01b201b5 01b801bb 01be01c1 01c401c7 01ca01cd 01d001d3 01d601d9 01f901fa 01fb01fc>
But when I try to create NSArray from data using NSKeyedUnarchiver - the app got crashed showing this error
-[__NSCFData objectForKey:]: unrecognized selector sent to instance
Where I am making the mistake ?
Please help me to retrieve the saved array, Thanks in advance.

Why do you use NSData? Once the string received, write below line
NSArray *myArrayFromDB = [MYstring componentsSeparatedByString:#","];
Try this if it is working. Let me know if not and I find another solution

NSData *dataFromArray = [NSKeyedArchiver archivedDataWithRootObject:myArray];
This creates an XML representation of myArray and places it in dataFromArray. Something like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
<plist version="1.0">
<array>
<string>It is a tale told by an idiot,</string>
<string>Full of sound and fury, signifying nothing.</string>
</array>
</plist>
The XML is converted to UTF8 format and stored in the NSData object.
When you do:
NSString *insertSQL = [NSString stringWithFormat: #"INSERT INTO ARRAY(PROFILE) VALUES (\"%#\")",dataFromArray];
the stringWithFormat operation interprets the format string. When it sees %#, it thinks "I must take the next object reference from the parameter list, apply the description method to it, and place that into the string."
The description method of NSData takes the <?xml version... data and converts it into displayable hex (since description is intended to be used purely for debugging, never "real" code). The displayable hex is formatted in groups of 8 characters, separated by blanks and enclosed in "<>" characters. By now the data is hopelessly garbled, and you haven't even inserted it into the DB yet.

Related

How to get data from Sqlite database?

I have create sqlite db Table as below
NSString * sqlStmt =#"CREATE TABLE IF NOT EXISTS SONGS (ID INTEGER PRIMARY KEY AUTOINCREMENT, SONGNAME TEXT UNIQUE NOT NULL,ALBUMNAME TEXT ,ARTIST TEXT ,SIZE FLOAT ,IMAGE BLOB)";
when i am trying to get data from this table getting nothing
NSString *sqlQuery = [NSString stringWithFormat:
#"SELECT ARTIST ,IMAGE FROM SONGS WHERE SONGNAME=kannulada"];
but iam getting data when iam trying to get data by id.
NSString *sqlQuery = [NSString stringWithFormat:
#"SELECT ARTIST ,IMAGE FROM SONGS WHERE id=1"];
when i debugging it not excuting prepare loop
-(NSMutableArray*)retrieveDataArrayWHICHISequql
{
[dataArray removeAllObjects];
sqlite3_stmt *statement;
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &_SQliteDB) == SQLITE_OK)
{
const char * selectQuery =[sqlQuery UTF8String];
if (sqlite3_prepare_v2(_SQliteDB, selectQuery, -1, &statement, NULL)== SQLITE_OK)
{
while (sqlite3_step(statement)== SQLITE_ROW)
{
NSString * addressStr = [[NSString alloc]initWithUTF8String:(const char*)sqlite3_column_text(statement, 0)];
NSString * phNumStr = [[NSString alloc]initWithUTF8String:(const char*)sqlite3_column_text(statement, 1)];
// NSString * nameStr = [[NSString alloc]initWithUTF8String:(const char*)sqlite3_column_text(statement, 2)];
NSMutableDictionary * dataDict = [[NSMutableDictionary alloc]init];
[dataDict setObject:addressStr forKey:#"getaddress"];
[dataDict setObject:phNumStr forKey:#"getphone"];
// [dataDict setObject:nameStr forKey:#"getname"];
[dataArray addObject:dataDict];
}
sqlite3_finalize(statement);
sqlite3_close(_SQliteDB);
}
}
return dataArray;
NSString *sqlQuery = [NSString
stringWithFormat:
#"SELECT ARTIST ,IMAGE FROM SONGS WHERE SONGNAME='kannulada'"]
;
kannulada is string , so you must use single quote
The problem is that if you have an error in sqlite3_prepare_v2 (it didn't return SQLITE_OK). So, if that happens, you should look at what sqlite3_errmsg returned, and it will tell you precisely why it failed. If you don't look at the meaningful error message that SQLite returns, you're flying blind.
if (sqlite3_prepare_v2(_SQliteDB, selectQuery, -1, &statement, NULL)== SQLITE_OK)
{
...
} else {
NSLog(#"SQL Error: %s", sqlite3_errmsg(_SQliteDB));
}
Regarding why it's failing, it's because you have to either quote the string literal, 'kannulada', or, better, use a SQL placeholder, ?, and then use sqlite3_bind_text.

How to Set Sqllite Result Set to insert Only Distinct Value in iOS?

i am newbie in iOS and also newbie in sqllite database so please help me for my Question. I want to Insert Only Distinct Value in sqllite Table, like as Here i Insert Value in to My Table
sqlite3_stmt *statement;
const char *dbpath = [_databasePath UTF8String];
if (sqlite3_open(dbpath, &_myDatabase) == SQLITE_OK) {
NSString *insertSQL = [NSString stringWithFormat:
#"INSERT INTO ALLREADTABLE (PostId, PostTitle, ImageLink, ShortDescription,Category,PostDate) VALUES (\"%#\", \"%#\", \"%#\", \"%#\",\"%#\",\"%#\")",
post_id, cell.headLabel.text, image,self.shortDiscription,cell.categoryLabel.text,cell.timeLabel.text];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(_myDatabase, insert_stmt,
-1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
NSString *string=[NSString stringWithFormat:#"Value Added %# %# %# %# %# %# ",post_id, cell.headLabel.text, image,self.shortDiscription,cell.categoryLabel.text,cell.timeLabel.text];
NSLog(#"String %#",string);
} else
{
NSLog(#"Failed to add contact");
}
sqlite3_finalize(statement);
sqlite3_close(_myDatabase);
}
Now i want when i insert same value into table then i want old value are deleted and new value are inserted in to my table.How it is Possible Please Give me Solution for it.

Saving entire NSArray in SQLite

I want to saving entire array into SQLite. I am getting an array having many other arrays in that.
Is it possible ? If so please let tell me.
I have tried this - But "ERROR TO SAVE DATA"
NSArray *array = [dict objectForKey:#"events_data"];
sqlite3_stmt *statement;
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
NSString *insertSQL = [NSString stringWithFormat: #"INSERT INTO CONTACTS (event_array) VALUES (\"%#\")", array];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(contactDB, insert_stmt, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
NSLog(#"DATA SAVED TO DB");
} else {
NSLog(#"ERROR TO SAVE DATA");
}
sqlite3_finalize(statement);
sqlite3_close(contactDB);
}
If it is possible please tell me how to retrieve it also.
You need not use sqlite directly in iOS. Indeed, you should wrap the persistent object in an NSManagedObject. To retrieve the object, simply create an NSFetchRequest, set its entity, and have the context execute it, which will return a standard NSArray of NSManagedObjects, which you can iterate over. Hope this helped, please leave a comment if you have further issues.

How to insert value in SQLite table [duplicate]

This question already has an answer here:
When I try SQLite on iOS, INSERT and UPDATE does not work
(1 answer)
Closed 9 years ago.
This is code I'm using to insert data into table,
sqlite3 *database;
NSString *dbPath = [[[NSBundle mainBundle] resourcePath ]stringByAppendingPathComponent:#"mobdb.sqlite"];
if(sqlite3_open([dbPath UTF8String],&database)==SQLITE_OK)
{
const char *sqlstatement = "INSERT INTO mobDetails (nMonId, nScore) VALUES (?,?)";
sqlite3_stmt *compiledstatement;
if(sqlite3_prepare_v2(database,sqlstatement , -1, &compiledstatement, NULL)==SQLITE_OK)
{
NSString * str1 =#"1";
NSString * str2 =#"12";
sqlite3_bind_int(compiledstatement, 1, [str1 integerValue]);
sqlite3_bind_int(compiledstatement, 2, [str2 integerValue]);
if(sqlite3_step(compiledstatement)==SQLITE_DONE)
{
NSLog(#"done");
}
else
{
NSLog(#"ERROR");
}
sqlite3_reset(compiledstatement);
}
else
{
NSAssert1(0, #"Error . '%s'", sqlite3_errmsg(database));
}
sqlite3_close(database);
}
Its shows "done" message but data not inserted into the table can any one help me for this.
also how to insert string in the table ?
The problem is that the app bundle is read-only (as you could have probably found out after 5 minutes of googling). Consequently, you can't insert to a database in the app bundle.
One thing that is wrong with the usage of the SQLite API is that you are calling sqlite3_reset() whereas sqlite3_finalize() should have been called. (Thanks #trojanfoe.)
(Oh, and this has absolutely nothing to do with Xcode at all.)
- (void) saveData
{
sqlite3_stmt *statement;
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
NSString *insertSQL = [NSString stringWithFormat:
#"INSERT INTO CONTACTS
(name, address, phone) VALUES (\"%#\", \"%#\", \"%#\")",
name.text, address.text, phone.text];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(contactDB, insert_stmt,
-1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
status.text = #"Contact added";
name.text = #"";
address.text = #"";
phone.text = #"";
} else {
status.text = #"Failed to add contact";
}
sqlite3_finalize(statement);
sqlite3_close(contactDB);
}
}

sqlite data retrieval from another view controller

i have a view Controller called as VegQuantity which does totalcost=(quantity*cost of the dish) and inserts the itemname,quantity,totalcost into a table called as FINALORDER with database name FinalOrder
sqlite3_stmt *statement;
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &FinalOrder) == SQLITE_OK)
{
NSString *insertSQL = [NSString stringWithFormat: #"INSERT INTO FINALORDER (itemname, quantity, totalcost) VALUES (\"%#\", \"%#\", \"%#\")", itemName.text, input.text, output.text];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(FinalOrder, insert_stmt, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
// status.text = #"Contact added";
// name.text = #"";
// address.text = #"";
// phone.text = #"";
NSLog(#"added");
} else {
NSLog(#"Couldnt add");
}
sqlite3_finalize(statement);
sqlite3_close(FinalOrder);
}
Final View Controller viewdidload method
const char *dbpath = [databasePath UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &FinalOrder) == SQLITE_OK)
{
NSString *querySQL = [NSString stringWithFormat: #"SELECT * FROM FINALORDER"];
const char *query_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(FinalOrder, query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *itemname = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 0)];
item.text = itemname;
NSString *qua = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 1)];
quantity.text = qua;
NSString *total = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 2)];
totalcost.text=total;
}
sqlite3_finalize(statement);
}
sqlite3_close(FinalOrder);
}
But i keep getting this error called expected expression before FinalOrder,and is it correct for me to write this code inside viewdidload? i dont have any button in Final view controller i have a button called order in a view controller called as Restaurant which actually shows me Final view controller..am i supposed to search for the db file again in the Final viewcontroller and i am sorry question seems kind of vague but in brief i just want to know how to retrieve and display the data which i have inserted in VegQuantity view controller into the final view controller thanks
I believe that the problem must rest in the definition of FinalOrder which, on the basis of the error message, looks like has been defined as a class, not as a sqlite3 * variable. Given that the scope of your usage of the database is limited to these two methods, I'd suggest defining a sqlite3 * within that scope, and use that, such as:
- (void)saveRecord
{
sqlite3 *database;
sqlite3_stmt *statement;
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &database) == SQLITE_OK)
{
[self purgeTable:database];
NSString *insertSQL = #"INSERT INTO FINALORDER (itemname, quantity, totalcost) VALUES (?, ?, ?)";
if (sqlite3_prepare_v2(database, [insertSQL UTF8String], -1, &statement, NULL) == SQLITE_OK)
{
sqlite3_bind_text(statement, 1, [itemName.text UTF8String], -1, NULL);
sqlite3_bind_int(statement, 2, [input.text intValue]);
sqlite3_bind_double(statement, 3, [output.text doubleValue]);
if (sqlite3_step(statement) == SQLITE_DONE)
{
// status.text = #"Contact added";
// name.text = #"";
// address.text = #"";
// phone.text = #"";
NSLog(#"added");
} else {
NSLog(#"%s Couldn't add; errmsg='%s'", __FUNCTION__, sqlite3_errmsg(database));
}
sqlite3_finalize(statement);
} else {
NSLog(#"%s Couldn't prepare; errmsg='%s'", __FUNCTION__, sqlite3_errmsg(database));
}
sqlite3_close(database);
}
}
Note, in addition to using a sqlite3 * variable for the database, to make this a little more robust:
I have replaced the stringWithFormat statement that built the SQL insert statement with an INSERT statement that uses the ? placeholders and then use sqlite3_bind_text to bind values to that statement. This way, if someone entered a value that included a quotation mark, the insert statement will still work (your original implementation would have crashed and/or was susceptible to a SQL injection attack).
I have also added the sqlite3_errmsg statements so if something goes wrong, I know what the problem was.
Rather than treating these three fields as text fields, I'm assuming your table is defined as CREATE TABLE IF NOT EXISTS FINALORDER (itemname TEXT, quantity INT, totalcost REAL); and therefore use text, int, and double bind statements.
This, incidentally, invokes a purgeTable method, so if you run it twice, it will remove the old record in there:
- (void)purgeTable:(sqlite3 *)database
{
if (sqlite3_exec(database, "DELETE FROM FINALORDER;", NULL, NULL, NULL) != SQLITE_OK)
NSLog(#"%s Couldn't purge table %s", __FUNCTION__, sqlite3_errmsg(database));
}
Anyway, you can then read this data via:
- (void)loadRecord
{
sqlite3 *database;
const char *dbpath = [databasePath UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &database) == SQLITE_OK)
{
NSString *querySQL = #"SELECT * FROM FINALORDER";
if (sqlite3_prepare_v2(database, [querySQL UTF8String], -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *itemname = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 0)];
item.text = itemname;
//[itemname release]; // if not ARC, uncomment this line
int qua = sqlite3_column_int(statement, 1);
quantity.text = [NSString stringWithFormat:#"%1d", qua];
double total = sqlite3_column_double(statement, 2);
totalcost.text = [NSString stringWithFormat:#"%1.2f", total];
}
sqlite3_finalize(statement);
} else {
NSLog(#"%s Couldn't prepare; errmsg='%s'", __FUNCTION__, sqlite3_errmsg(database));
}
sqlite3_close(database);
}
}
Note,
I've added a sqlite3_errmsg log statement if the sqlite3_prepare fails, and I've retired the stringWithFormat (because you weren't formatting anything).
Given that quantity was INT and total was REAL, I'm retrieving the values using the appropriate variation of sqlite3_column_text, sqlite3_column_int, or sqlite3_column_double, as appropriate.
I infer that you're not using ARC and therefore, the [NSString alloc] must have an associated release or autorelease.
Finally, in our chat, you said that you received an error message (presumably an "Undefined Symbols" message) that said:
OBJC_CLASS_$"_FinalOrder"
This means that you are trying to use an object class of FinalOrder, but you haven't defined that class anywhere. Take a look at which .o files it's reporting this error for and look at the corresponding .m file, and look for your use of the FinalOrder class there. You clearly have a FinalOrder class interface defined somewhere, but never defined the class implementation, or, if you have one, for some reason it's not included in your target's "Compile Sources" listing, so double check it's here:
Finally, by the way, make sure your database is in the Documents folder, not trying to open a copy of the database in your project's bundle (because that's read only). If you have a copy of the database in your bundle, just check to see if you already have it in you Documents folder, and if not, copy it from the bundle to the Documents folder.

Resources