unable to save image in SQLite in iPhone - objective-c

can anybody tell me why i am unable to insert an image in sqlite. Some time i get error like:
1)(sqlite3_step(stm) == SQLITE_DONE) , getting value as 21.
2)gets stored in an array and not into databse.
I a fresher in objective c so if u could post back a code it would be sweet, as i searched almost evry site.
Thanks!
-(IBAction)submitDetails:(id)sender
{
sqlite3_stmt *stm;
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &connectDB) == SQLITE_OK)
{
UIImage *contactImage = imageView.image;
NSData *imageData = UIImageJPEGRepresentation(contactImage, 100);
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:imageData forKey:#"image"];
[defaults synchronize];
NSLog(#"Data saved");
NSString *insertSQL = [NSString stringWithFormat: #"INSERT INTO STUDENTS (name,salary, gid,photo) VALUES (\"%#\", \"%#\", \"%#\",?)", name.text,salary.text,gid.text,imageData] ;
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(connectDB, insert_stmt, -1, &stm, NULL);
NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirec = [paths objectAtIndex:0];
NSString *imgePath = [documentsDirec stringByAppendingPathComponent:#"note.sqlite"];
if(sqlite3_open([imgePath UTF8String], &database) == SQLITE_OK){
const char *sql = "insert into images (images) values (?)";
if(sqlite3_prepare_v2(database, sql, -1, &addStmt, NULL) == SQLITE_OK){
UIImage *edtedImae = [info objectForKey:UIImagePickerControllerOriginalImage];
NSData *dataForImage = UIImagePNGRepresentation(edtedImae);
sqlite3_bind_blob(addStmt, 1, [dataForImage bytes], [dataForImage length], SQLITE_TRANSIENT);
NSLog(#"1st ..... %i",sqlite3_step(stm));
NSLog(#" 2nd.... %i",SQLITE_DONE);
if (sqlite3_step(stm) == SQLITE_DONE)
{
NSString* aName=name.text;
NSString* aSalary=salary.text;
NSString* aGid=gid.text;
UIImage* aPhoto=[UIImage imageWithData:imageData];
Person *person = [[Person alloc] initWithName:aName salary:aSalary gid:aGid photo:aPhoto];
[rootObject.list addObject:person];
[person release];
status.text = #"Contact added";
NSLog(#"contact added %# %#",name.text,gid.text);
NSLog(#"the count in person list is %i",[rootObject.list count]);
name.text = #"";
gid.text = #"";
salary.text = #"";
}
else
{
status.text = #"Failed to add contact";
}
sqlite3_finalize(stm);
sqlite3_close(connectDB);
}
}

Don't save the image in the database if you can avoid it. Instead, use columns to store the file name and any other attributes and then save the image as a document. I use a guid method to create file names. Don't forget to remove the document if you should remove the db entry.

Related

Save and retrieve AttributedString to SQLite3

I have a project in Objective-c in which I am trying to find a way of saving the attributedText from a UITextView to a SQLite3 table.
My Project Target OS is 12.1.
I am using an object called "MMItem" with a NSData property called "notesAttributed".
In my viewController Class I am using NSKeyedArchiver to encode the AttributedText into a NSdata format then copying to the object property.
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.itemNotes.attributedText requiringSecureCoding:YES error:Nil];
self.item.notesAttributed = data;
I then call a method in my model to save the object
NSString *resultStr = [self.meetingModel saveAttributedItemNote:item];
In the model I'm attempting to save the attributed string to a field in the Item table setup as type 'blob'
- (NSString *)saveAttributedItemNote:(MMItem *)item{
NSString *errorMessage;
NSString *sql;
NSInteger result = 0;
if (item) {
//create blob encoded data for Attributed notes
NSInteger itemID = item.itemID;
sql = [NSString stringWithFormat:
#"UPDATE Item set noteAttributed = %# WHERE itemID = %ld",item.notesAttributed, (long)itemID];
char *err;
// open DB and save
if ([self openDB]){
//NSLog(#"%#", NSStringFromSelector(_cmd));
result = sqlite3_exec(db, [sql UTF8String], NULL, NULL, &err);
sqlite3_close(db);
}
if (result != SQLITE_OK)
{
errorMessage = #"Error";
}
else
{
//NSLog(#"Table updated");
errorMessage = #"OK";
[self saveAttributedItemNote:item];
}
}
item = nil;
return errorMessage;
}
The SQL Execute statement fails with error 1.
I suspect I'm meant to insert the blob into the table using 'binding' and not an Execute statement but I just find how to put this all together using Objective-c.
Any help would be great thanks.
With acknowledgement to the following post iOS SQLite Blob data is saving NULL
I managed to get a working solution. It's apparent that to save a blob into an SQLite you must use a Prepare statement with a sqlite3_bind_blob to insert the blob parameters into the statement, then to use sqlite3_step to deploy it.
This then allows the bytes and length parameters to also be passed into the statement, which I don't think can be done using the execute method I was originally trying.
Here is the code that works perfectly.
- (NSString *)saveAttributedItemNote:(MMItem *)item{
NSString *errorMessage;
if (item) {
//create blob encoded data for Attributed notes
int itemID = (int)item.itemID;
if ([self openDB]) {
const char *insert_stmt = "UPDATE Item set notesAttributed = ? WHERE itemID = ?";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(db, insert_stmt, -1, &statement, NULL) == SQLITE_OK) {
sqlite3_bind_blob(statement, 1, item.notesAttributed.bytes, (int)item.notesAttributed.length, SQLITE_TRANSIENT);
sqlite3_bind_int(statement, 2, itemID);
if (sqlite3_step(statement) == SQLITE_DONE) {
errorMessage = #"OK";
//update successfull
}else{
const char *err = sqlite3_errmsg(db);
NSString *error = [[NSString alloc] initWithUTF8String:err];
NSLog(#"Update error ID:%#",error);
errorMessage = #"Error";
}
sqlite3_finalize(statement);
}else{
errorMessage = #"Error";
NSLog(#"Unable to prepare stsement %s: %s",insert_stmt, sqlite3_errmsg(db));
}
sqlite3_close(db);
}
}
item = nil;
return errorMessage;
}
The following code then shows how the field was retrieved from sqlite. Column 11 is the blob.
if (sqlite3_prepare(db, [sql UTF8String], -1, &statement, nil)==SQLITE_OK)
{
//NSLog(#"SQL Select OK");
while (sqlite3_step(statement)==SQLITE_ROW) {
MMItem *item = [[MMItem alloc]init];
item.itemID = sqlite3_column_int(statement, 0);
item.topicID = sqlite3_column_int(statement, 1);
item.sequenceID = sqlite3_column_int(statement, 2);
char *description = (char *) sqlite3_column_text(statement, 3);
if(description) item.itemDescription = [[NSString alloc]initWithUTF8String:description];
char *notes = (char *) sqlite3_column_text(statement, 4);
if(notes) item.notes = [[NSString alloc]initWithUTF8String:notes];
char *actionBy = (char *) sqlite3_column_text(statement, 5);
if(actionBy) item.actionBy = [[NSString alloc]initWithUTF8String:actionBy];
char *requiredBy = (char *) sqlite3_column_text(statement, 6);
if (requiredBy) item.requiredBy = [[NSString alloc]initWithUTF8String:requiredBy];
item.completed = sqlite3_column_int(statement, 7);
char *proposedBy = (char *) sqlite3_column_text(statement, 8);
if (proposedBy) item.proposedBy = [[NSString alloc]initWithUTF8String:proposedBy];
char *secondedBy = (char *) sqlite3_column_text(statement, 9);
if (secondedBy) item.secondedBy = [[NSString alloc]initWithUTF8String:secondedBy];
item.carried = sqlite3_column_int(statement, 10);
NSData *attributedTextData = [[NSData alloc]initWithBytes:sqlite3_column_blob(statement,11) length:sqlite3_column_bytes(statement, 11)];
item.notesAttributed = attributedTextData;
[items addObject:item];
item = nil;
}
}
Then in the ViewController, the following was used to take the NSData property and decode this for the UITextView (self.itemNotes)
self.itemNotes.attributedText = [NSKeyedUnarchiver unarchivedObjectOfClass:([NSAttributedString class]) fromData:self.item.notesAttributed error:&error];
Weird how just posting the question lead me to finding the right solution. Thanks to El Tomato for your help bud.

Objective C Sqlite Update Query Based on ID

Trying to learn Sqlite without using a wrapper and have managed most things but am really stuck on UPDATE queries
I am trying to update a sting in one column based on its _ID number which is the primary Key and is unique.
I have tried all sorts of code from all over google . This one says it has worked but when I check the column has not been updated
here is the code
NSDateFormatter *formatter;
NSString *dateString;
formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"dd-MM-yyyy HH:mm"];
dateString = [formatter stringFromDate:[NSDate date]];
NSString *IDCODE = code.stringValue;
NSInteger b = [IDCODE integerValue];
sqlite3 *contactDB; //Declare a pointer to sqlite database structure
const char *dbpath = [dbPath UTF8String]; // Convert NSString to UTF-8
if (sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
//Database opened successfully
NSString *databaseName = #"vistorlog.db";
// Get the path to the documents directory and append the databaseName
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
NSString *databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
const char *dbPath=[databasePath UTF8String];
if (sqlite3_open(dbPath, &myDB)==SQLITE_OK) {
NSLog(#"database Opened");
const char* updateQuery="update LOG set TIMEOUT='22/03/19' where _ID=1";
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(myDB, updateQuery, -1, &stmt, NULL)==SQLITE_OK) {
NSLog(#"Query Executed");
}else{
NSLog(#"Query NOT Executed");
}
}
sqlite3_close(myDB);
}
else {
//Failed to open database
}
It opens the DB vistorlog.db ok. There is a table called LOG and there is column called TIMEOUT which is where I want the string to be updated and there is a column called _ID which is what Im basing the query on yet it won't update
eventually I want to have the update statement use the string variable dateString as the string to update and b as the integer variable for the _ID
any ideas where Im going wrong?
Any help appreciated
Mark
EDIT
WORKING CODE TO UPDATE A QUERY
sqlite3 *contactDB; //Declare a pointer to sqlite database structure
const char *dbpath = [dbPath UTF8String]; // Convert NSString to UTF-8
if (sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
//Database opened successfully
NSString *databaseName = #"vistorlog.db";
// Get the path to the documents directory and append the databaseName
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
NSString *databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
const char *dbPath=[databasePath UTF8String];
if (sqlite3_open(dbPath, &myDB)==SQLITE_OK) {
NSLog(#"database Opened");
// define dateString and IDCODE as strings before this
const char *updateQuery = "Update log set TIMEOUT=? where _ID=?";
sqlite3_stmt *stmt;
// Prepare Stment
if (sqlite3_prepare_v2(myDB, updateQuery, -1, &stmt, NULL) == SQLITE_OK) {
sqlite3_bind_text(stmt, 1, [dateString UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2, [IDCODE UTF8String], -1, SQLITE_TRANSIENT); if(sqlite3_step(stmt) == SQLITE_DONE) {
NSLog(#"Query Executed");
} else {
NSLog(#"Query NOT Executed: %s", sqlite3_errmsg(myDB));
}
sqlite3_finalize(stmt);
}else{
NSLog(#"Statement NOT Prepared: %s", sqlite3_errmsg(myDB));
}
}
sqlite3_close(myDB);
}
else {
//Failed to open database
}
Don't forget to import sqlite3.h
Mark
You are only preparing the statement but not actually executing it.
You need to execute sqlite3_step() after you execute sqlite3_prepare_v2
if (sqlite3_prepare_v2(myDB, updateQuery, -1, &stmt, NULL) == SQLITE_OK) {
if(sqlite3_step(stmt) == SQLITE_DONE) {
NSLog(#"Query Executed");
} else {
NSLog(#"Query NOT Executed: %s", sqlite3_errmsg(myDB));
}
sqlite3_finalize(stmt);
}else{
NSLog(#"Statement NOT Prepared: %s", sqlite3_errmsg(myDB));
}

Xcode - NSInvalidArgumentException', reason: '*** +[NSString stringWithUTF8String:]: NULL cString'

I have the below code that should populate the TableViewController with information from my sqlite file, it lets me add fine, and i can view the file and the information is there, but I'm getting the above error message, and failing miserably at fixing it....
-(NSMutableArray *) stockList
{
NSString *filePath = [self getWritableDBPath];
if(sqlite3_open([filePath UTF8String], &db) == SQLITE_OK)
{
const char *sql = "Select Description, Quantity from StockTable";
sqlite3_stmt *sqlStatement;
if(sqlite3_prepare(db, sql, -1, &sqlStatement, NULL) != SQLITE_OK)
{
NSLog(#"Problem with query: %s", sqlite3_errmsg(db));
}
else
{
while (sqlite3_step(sqlStatement)==SQLITE_ROW)
{
Stock * stock = [[Stock alloc] init];
stock.desc = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 1)];
stock.qty = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 2)];
[thestock addObject:stock];
stock = nil;
}
}
sqlite3_finalize(sqlStatement);
}
sqlite3_close(db);
return thestock;
}
thanks for any help, currently googling it myself..
connection strings as mentioned below: (reason being it causes a LINK error and states that MyDB is a duplicate in both views)
TableView:
NSString * MyDB2=#"StockDatabase.db";
AddingView:
NSString * MyDB=#"StockDatabase.db";
One (or both) of the columns you are fetching from the database is NULL:
stock.desc = [NSString stringWithUTF8String:(char *)sqlite3_column_text(sqlStatement, 1)];
stock.qty = [NSString stringWithUTF8String:(char *)sqlite3_column_text(sqlStatement, 2)];
Guard against that with:
const char *desc = sqlite3_column_text(sqlStatement, 1);
if (desc)
stock.desk = #(desc);
const char *qty = sqlite3_column_text(sqlStatement, 2);
if (qty)
stock.qty = #(qty);
I got this error too. How I solved it is by setting the first column text index to "0" instead of "1". And the error went away.
char *charPrice = (char*) sqlite3_column_text(stmt, 0);
NSString *price = [NSString stringWithUTF8String:charPrice];
char *charName = (char*) sqlite3_column_text(stmt, 1);
NSString *name = [NSString stringWithUTF8String:charName];

call an array method

I have this method, and I am trying to call that method doing [self getUsers ObjectAtIndex:0] but compiler won't compile that line at all and says it's unknown. How do I call an array method to display all the string in it?
- (NSArray*)getUsers
{
NSArray *getUsers = [[NSArray alloc] init];
NSString *databasepath;
NSString *docsDir;
NSArray *dirPaths;
//NSString *databasePath;
//get the document directory
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
//build the path to the database file
databasepath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent:
#"users.db"]];
const char *dbPath = [databasepath UTF8String];
sqlite3_stmt *statement;
sqlite3 *userdb;
if (sqlite3_open(dbPath, &userdb) == SQLITE_OK)
{
NSString *querySQL = [NSString stringWithFormat:#"SELECT * FROM USERS WHERE username = \"%#\"", txtUsername.text];
const char *sql_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(userdb, sql_stmt, -1, &statement, NULL) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW)
{
char *field = (char*) sqlite3_column_text(statement, 1);
NSString *usernameField = [[NSString alloc]initWithUTF8String:field];
//NSLog(#"%#", usernameField);
getUsers = [NSArray arrayWithObject:usernameField];
}
}
sqlite3_finalize(statement);
}
sqlite3_close(userdb);
return getUsers;
}
Use [[self getUsers] objectAtIndex:0].
If you use the latest Xcode/clang version, you can also use [self getUsers][0], since arrays now support indexing.
Consider say you had a method -(id)someMethod, and then called it like this:
id myValue = [self someMethod];
Inside someMethod, there is a return statement - whatever is to the right of the return is what will be assigned to myValue in the calling method or function.
Just call like this:
NSArray *arrAllUsers = [self getUsers]; //As getUsers is method which returns nsarray.

How to call different tables in an "if and else" part of the same database in iPhone?

I have a database. It has two tables in it. I want to call one table in an if condition. How do I call the second table in the else part if the if conditions fail?
This is the code I used:
{
NSArray *Paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *DocumentsDirectory = [Paths objectAtIndex:0];
NSString *Path = [DocumentsDirectory stringByAppendingPathComponent:#"StoreList.sqlite"];
// Open the database from the users filessytem.
if (sqlite3_open([Path UTF8String], &database) == SQLITE_OK) {
// Setup the SQL Statement and compile it for faster access
//*********const char *sqlStatement = "select * from Store where Zipcode ='%#'",inputTxt ;
NSString *sqlStatement = [NSString stringWithFormat:#"select * from Store where Zipcode ='%#' or Address = '%#' or CityName = '%#'",inputTxt, inputTxt, inputTxt];
NSLog(#" Query in if :%#",sqlStatement);
sqlite3_stmt *compiledStatement;
if (sqlite3_prepare_v2(database, [sqlStatement UTF8String], -1, &compiledStatement, NULL) == SQLITE_OK) {
// Loop through the results and add them to the feeds array.
if(sqlite3_step(compiledStatement) == SQLITE_ROW) {
// Read the data from the result row
NSString *latValue = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 2)];
NSLog(#"Latitude:%#",latValue);
NSString *longValue = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 3)];
NSLog(#"Longitude:%#",longValue);
//currentLocationLbl.text=[NSString stringWithFormat:#" %# , %#" ,latValue,longValue];
// delegate.latitudeVal=latValue;
// delegate.longitudeVal=longValue;
txtChangeLocation.text = #"";
isFromChangeLoc=TRUE;
//self.tabBarController.selectedIndex=3;
}
else {
NSLog(#"ELSE PART");
// Open the database from the user's filessytem.
if (sqlite3_open([Path UTF8String], &database) == SQLITE_OK) {
// Setup the SQL Statement and compile it for faster access
//*********const char *sqlStatement = "select * from Store where Zipcode ='%#'",inputTxt ;
NSString *sqlStatement = [NSString stringWithFormat:#"select * from zipcodes where zip ='35004'"];
NSLog(#" Query in if :%#",sqlStatement);
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, [sqlStatement UTF8String], -1, &compiledStatement, NULL) == SQLITE_OK) {
// Loop through the results and add them to the feeds array
if(sqlite3_step(compiledStatement) == SQLITE_ROW) {
// Read the data from the result row
NSString *latValue = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 2)];
NSLog(#"Latitude:%#",latValue);
NSString *longValue = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 3)];
NSLog(#"Longitude:%#",longValue);
//currentLocationLbl.text=[NSString stringWithFormat:#" %# , %#" ,latValue,longValue];
// delegate.latitudeVal=latValue;
// delegate.longitudeVal=longValue;
txtChangeLocation.text = #"";
isFromChangeLoc=TRUE;
}
}
}
}
sqlite3_finalize(compiledStatement);
sqlite3_close(database);
}
}
I'm getting the input from the text box. When I give the correct value, which is there in the database, it is working fine, the fetch of the data is correct. If I give the wrong data in the textbox it's not working fine - the else condition fails.
How can this issue be fixed?
When going on else you open the same database again, that's not needed (and might cause some problems as well, haven't tried it). Use a BOOL when running the first (select) statement and set it to YES or NO if it fails or not. After you finish with the first statement check the BOOL value and if ==NO run the second statement.
...
BOOL success = NO;
NSString *sqlStatement = [NSString stringWithFormat:#"select * from Store where Zipcode '%#' or Address = '%#' or CityName = '%#'",inputTxt, inputTxt, inputTxt];
NSLog(#" Query in if :%#",sqlStatement);
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, [sqlStatement UTF8String], -1, &compiledStatement, NULL) == SQLITE_OK){
if(sqlite3_step(compiledStatement) == SQLITE_ROW) {
// Read the data from the result row
NSString *latValue = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 2)];
NSLog(#"Latitude:%#",latValue);
NSString *longValue = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 3)];
NSLog(#"Longitude:%#",longValue);
//currentLocationLbl.text=[NSString stringWithFormat:#" %# , %#" ,latValue,longValue];
// delegate.latitudeVal=latValue;
// delegate.longitudeVal=longValue;
txtChangeLocation.text = #"";
isFromChangeLoc=TRUE;
//self.tabBarController.selectedIndex=3;
success=YES;
}
}
sqlite3_finalize(compiledStatement);
if(success){
//do the else from your code
NSString *sqlStatement = [NSString stringWithFormat:#"select * from zipcodes where zip ='35004'"];
NSLog(#" Query in if :%#",sqlStatement);
if(sqlite3_prepare_v2(database, [sqlStatement UTF8String], -1, &compiledStatement, NULL) == SQLITE_OK){
// Loop through the results and add them to the feeds array
if(sqlite3_step(compiledStatement) == SQLITE_ROW) {
// Read the data from the result row
NSString *latValue = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 2)];
NSLog(#"Latitude:%#",latValue);
NSString *longValue = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 3)];
NSLog(#"Longitude:%#",longValue);
//currentLocationLbl.text=[NSString stringWithFormat:#" %# , %#" ,latValue,longValue];
// delegate.latitudeVal=latValue;
// delegate.longitudeVal=longValue;
txtChangeLocation.text = #"";
isFromChangeLoc=TRUE;
}
}
sqlite3_finalize(compiledStatement);
}
I haven't run it, so it might contain some bugs.