I have a problem with the tab bar controller. My application is composed of a tab bar controller with two views. In the first I insert a record into a sqlite database, and in the second there is a table view that contains all the database records. I would like that when I insert a new record in the db, the table view was updated. This happens only in the method viewDidLoad, namely only one time. Is there a way to know when the tab bar item changes and then update the table? I had tried to use the properties: create an object of the ViewController class, and access to the property from an other ViewController...but I couldn't.
Thanks
//Insert new record view
sqlite3_stmt *statement;
const char*dbpath=[databasePath UTF8String];
if(sqlite3_open(dbpath, &DB) == SQLITE_OK)
{
NSString *insertSQL=[NSString stringWithFormat:#"INSERT INTO Example(Id, name) VALUES (?,?)"];
const char *insert_stmt=[insertSQL UTF8String];
if(sqlite3_prepare(DB, insert_stmt, -1, &statement, NULL) == SQLITE_OK )
{
sqlite3_bind_text(statement, 1, [id UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statement, 2, [name.text UTF8String], -1, SQLITE_TRANSIENT);
}
if(sqlite3_step(statement) == SQLITE_DONE)
{
NSLog(#"OK");
}
else
{
NSLog(#"Error");
}
sqlite3_finalize(statement);
sqlite3_close(DB);
}
else
{
NSLog(#"Error");
}
SecondViewController *svc=[SecondViewController alloc] initWithNibname:#"Second" bundle:nil];
[svc.tableView reloadData];
At the end of where you're inserting your record in the db, you could send out a notification that will alert SecondViewController
Start by replacing
SecondViewController *svc=[SecondViewController alloc] initWithNibname:#"Second" bundle:nil];
[svc.tableView reloadData];
With:
[[NSNotificationCenter defaultCenter] postNotificationName:#"DidUpdateDatabase" object:self];
And in viewDidLoad of SecondViewController add this block to listen for the changes:
[[NSNotificationCenter defaultCenter] addObserverForName:#"DidUpdateDatabase"
object:nil
queue:[NSOperationQueue currentQueue]
usingBlock:^(NSNotification *note) {
[self.tableView reloadData];
}];
This is called a notification pattern that allows objects to be decoupled from each other and still hold communication.
EDIT:
Another solution is in SecondViewController's viewDidAppear call [self.tableView reloadData]. This will cause it to reload every time the user goes to that view
Related
I am trying to update some values to my database using async task but I get this error:
Values not inserted. Error: SQL logic error or missing database
Here is the code I use to update the db:
- (void)persistResponseData:(NSDictionary *)data database:(DataBaseHandler *)database completion:(void (^)(NSError *error))completion {
NSDictionary *contentData = [data objectForKey:#"contentdata"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Process Contents
NSArray *contents = [contentData objectForKey:#"contents"];
if (contents != nil) {
[self storeData:contents inTable:#"contents" database:database withCustomIdentifier:nil];
}
});
}
The error occurs on this line of code with error code EXC_BAD_ACCESS:
if (sqlite3_prepare_v2(_database, [query UTF8String], -1, &statement, nil)==SQLITE_OK)
which is called when I try to confirm if a record exists or not, inside the storeData function.
If I remove the asynchronous part
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
then the code runs without any problem and the database gets updated.
Any idea how to resolve this ?
I have implemented NSOPERATIONQUEUE By sending request and i can able to download response from server but the problem is i cant able to parallely process the sqlite to insert/update the response,i am getting Database Locked Error.So Can anyone suggest me a solution to work with database for insertion/updation of my downloaded response??
I have created a NSOperation Class as PageLoadOperation and i will pass request 7 times to that NSOperation class and add it to queue as below
PageLoadOperation *pageLoad=[[PageLoadOperation alloc]initWithRequest:theRequest];
[queue addOperation:pageLoad];
In Main i can able to send request by below NSURLConnection
[NSURLConnection sendAsynchronousRequest:theRequest
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
[self openConnection];
[self doSomethingWithData:data];
[self closeConnection];
}];
and in doSomethingWithData method i will insert/update the response by converting data to NSString I have separately created method for opening the database connection and once insertion/updation completed i will closeconnection by following methods
-(BOOL)openConnection
{
database=nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
documentsDir = [paths objectAtIndex:0];
if(sqlite3_open([[documentsDir stringByAppendingPathComponent:#"DataStore"] UTF8String], &database)==SQLITE_OK)
{
NSLog(#"DB Connection Established successfully");
return YES;
}
else
{
NSLog(#"DB not connected");
return NO;
}
}
-(BOOL)closeConnection
{
NSLog(#"%#",documentsDir);
if(sqlite3_close(database)==SQLITE_OK)
{
NSLog(#"DB Connection closed successfully");
return YES;
}
else
{
NSLog(#"Error Failed to close DB because %s",sqlite3_errmsg(database));
return NO;
}
}
My Insertion Method is :
- (void) addToDatabase :(NSString*) sqlstmt :(NSArray *)params
{
sqlite3_stmt *addStmt;
if(sqlite3_prepare_v2(database, [sqlstmt UTF8String], -1, &addStmt, NULL) != SQLITE_OK)
NSAssert1(0, #"Error while creating add statement. '%s'", sqlite3_errmsg(database));
else
{
for (i=1; i<=[params count]; i++)
{
sqlite3_bind_text(addStmt, i,[[params objectAtIndex:i-1] UTF8String], -1, SQLITE_TRANSIENT);
}
}
if(SQLITE_DONE != sqlite3_step(addStmt))
NSAssert1(0, #"Error while inserting data. '%s'", sqlite3_errmsg(database));
else
NSLog(#"%lld",sqlite3_last_insert_rowid(database));
sqlite3_finalize(addStmt);
}
While adding to database i am getting database locked error because while one response is open the database and inserting into database parallely another response try to open the database connection,so how can i resolve this problem?
I need to store user doubleTap count in db. Its working fine.
If i am doing very fast double tapping to insert, issues will appear (database is locked, no such table: and out of memory). How to handle that issue?
Thanks in Advance
I tried this, FYI:
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void)
{
#try {
if([self openAppDatabase] == YES) //If DB open success...
{
sqlite3_stmt *statement = nil;
NSString *queryString = [NSString stringWithFormat:#"INSERT INTO %# (%#,%#) values (?,?)",Tables_Count,lkt_TO,ttt_TIME];
BOOL prepareStatementResult = sqlite3_prepare_v2(databaseObj, [queryString UTF8String], -1, &statement, NULL);
if(prepareStatementResult == SQLITE_OK) {
sqlite3_bind_text(statement, 1, [contactNo UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statement, 2, [sentDate UTF8String], -1, SQLITE_TRANSIENT);
if (sqlite3_step(statement) != SQLITE_DONE) {
DLog(#"Can't insert ---- Error");
}
}
else {
// In the database cannot be opened then show the error message on the debugger.
DLog(#"%s", sqlite3_errmsg(databaseObj));
}
// Release the compiled statement from memory.
sqlite3_finalize(statement);
}
[self closeAppDatabase]; //If DB not opened will close it here...
}
#catch (NSException *exception) {
DLog(#"#Exception --- %#",exception.reason);
}
}
The dispatch queue you are using is a concurrent queue, not a series queue. The result is that too many requests are being made to the dB at a time. You should be ok if you change your code to use dispatch_barrier_async but I've never used it on system queues.
Additionally, you're code could use some cleanup. The try is unnecessary since this code does not throw exceptions. Contrary to Java, Objective Chicago very rarely throws exceptions.
Lastly, you may consider using a Objective C SQLite wrapper, like FMDatabase. It cleans things up quite a bit.
Iam inserting the contacts from the address book in to the sqlite3 database. for only some contacts my app is getting crashed at
sqlite3_finalize(statement)
this statement. and this is happening in ios7. The crash log is as follows
"Critical failure: the LastResort font is unavailable. in ios"
NSString *insertSQL = [NSString stringWithFormat:#"insert into vcards (some thing data)",(something data)];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(database, insert_stmt,-1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
NSLog(#"Record inserted");
}
else {
NSLog(#"Record inserted failed");
}
sqlite3_finalize(statement);
sqlite3_close(database);
please any one suggest me how to fix this crash.
This has nothing to do with your contacts but to do with the fonts when transitioning from ios 6 to ios 7. Delete all the fonts in your project and then carefully add one by one to your .plist testing each time before adding the next until you find the one that is causing the problem.
Have a look at this post from someone who also had the same problem as you and the person's answer.
Person have the same problem as you
I spent hours trying to fix that but I've just given up; I have no idea what's wrong.
In my app, I do nested SQL operations to set all my objects correctly. For some reason, sometimes the sqlite3 objects do not get release properly, causing the memory to go up the roof. I understand it is a problem with using correctly sql3_close and sql3_finalize. However, as you will see, I think I have used them correctly.
Here is the source of the problem:
- (NSArray *) getAllSessions {
if (sqlite3_open(dbPath, &db) == SQLITE_OK) {
if (sqlite3_prepare_v2(db, query_stmt, -1, &statement, NULL) == SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW) {
//I found out that doing something like that
//toAdd.in_loc = [self getIndoorLocationWithId:[NSNumber numberWithInt:(int)sqlite3_column_int(statement, 6)]];
//messes the memory up all the time
//but doing that works OK:
NSNumber *_id = [[NSNumber alloc] initWithInt:(int) sqlite3_column_int(statement, 5)];
toAdd.out_loc = [self getOutdoorLocationWithId:_id];
[_id release];
//So I did the same with the second one, but this one messes memory up:
NSNumber *id2 = [[NSNumber alloc] initWithInt:(int)sqlite3_column_int(statement, 6)];
toAdd.in_loc = [self getIndoorLocationWithId:id2];
[id2 release];
}
sqlite3_finalize(statement);
}
sqlite3_close(db);
}
}
So here is the one that messes memory up:
- (IndoorLocation *) getIndoorLocationWithId:(NSNumber *) locId {
if (sqlite3_open([databasePath UTF8String], &db) == SQLITE_OK) {
if (sqlite3_prepare_v2(db, query_stmt, -1, &statement, NULL) == SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW) {
//if I comment the thing below it works
NSNumber *_id = [[NSNumber alloc] initWithInt:(int) sqlite3_column_int(statement, 5)];
toReturn.outLoc = [self getOutdoorLocationWithId:_id];
[_id release];
}
sqlite3_finalize(statement);
}
sqlite3_close(db);
}
}
So in the one that messes memory up, I use exactly the same function as the first time (getOutdoorLocationwithId), in the same way but it doesn't work, sqlite3 objects don't get released properly.
I hope you understand my problem, this is driving me nuts!
In a single user app like this, there's no need to continually open and close. This is not like a server based app where you have connection pooling with multiple users where holding the connection can defeat the pool and hurt scalability. In a phone app open it and hold it open. close it when you're done. At a minimum, do that within a recursive call.
To make it worse, you're opening and closing within recursive calls - just leave it open.
Also:
You're not checking the return codes of finalize
You're not checking the return codes of close.
You're preparing (compiling) the statments but you're not saving them off - call reset on the saved statement and execute it again.
Consider using FMDB - it's a good wrapper.
BTW, here's a richer and more durable close but don't use it on every call. Close it when you're ending or your app is going into the background ... This is mine which is similar to what fmdb does.
- (void)close
{
if (_sqlite3)
{
ENInfo(#"closing");
[self clearStatementCache];
int rc = sqlite3_close(_sqlite3);
ENDebug(#"close rc=%d", rc);
if (rc == SQLITE_BUSY)
{
ENError(#"SQLITE_BUSY: not all statements cleanly finalized");
sqlite3_stmt *stmt;
while ((stmt = sqlite3_next_stmt(_sqlite3, 0x00)) != 0)
{
ENDebug(#"finalizing stmt");
sqlite3_finalize(stmt);
}
rc = sqlite3_close(_sqlite3);
}
if (rc != SQLITE_OK)
{
ENError(#"close not OK. rc=%d", rc);
}
_sqlite3 = NULL;
}
}
I met the same issue.
When i use and open FMDB, it works OK. but in other callbacks it failed and threw exception of "Closing leaked statement." Finally I found that's because i kept the pointer from [FMDatabase databaseWithPath:dbPath] and the pointer is a autorelease object. I fixed the issue by this:
FMDatabase *db = [[FMDatabase databaseWithPath:dbPath] retain];
and when you want to close the database:
db [close];
[db release];
db = nil;
By this, you don't always open and close database for each operation, the should be a manager object accounting for it. When the manager starts, the database is open always until the manager stops.