SQLite data showing - objective-c

I'm totally new with SQLite. I have a UITableview with contains different days in it. (Monday till sunday). When i click on for example Monday an other viewcontroller contains with also a UITableview inside it. In the same viewcontroller i have a UIButton when i click on it i can add data to my SQLite database [A], i insert the name and the day of the week (The day of the week is in this example 'monday' that's because i clicked on the monday view controller).
When i insert a name it appears in my tableview. But when i go back to my first viewcontroller with the days and i click for example on Wednesday the data i added also appear there.
So my question is; How can i show the name which i inserted in monday, only in the monday tableview and not the other days(tableviews)
More information:
So when a user adds a name in 'monday' i send the dayoftheweek with the added name to the SQLite database, when a user adds a name in wednesday i send 'dayoftheweek' Wednesday etc..
Database Coffee looks like =
CoffeeName | dayoftheweek
-------------------------
Hello world | Monday
Hello Planet | Wednesday
Hello Animal | Monday
Hello STOVW | Friday
[A] const char *sql = "insert into Coffee(CoffeeName, dayoftheweek) Values(?, ?)";
I need to check if the day (for example) monday is the same as dayoftheweek (monday) and then display al the items which contains 'dayoftheweek monday'
My sqlite looks like:
+ (void) getInitialDataToDisplay:(NSString *)dbPath {
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
const char *sql = "select coffeeID, coffeeName from coffee";
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
NSInteger primaryKey = sqlite3_column_int(selectstmt, 0);
Coffee *coffeeObj = [[Coffee alloc] initWithPrimaryKey:primaryKey];
coffeeObj.LessonName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)];
coffeeObj.dayoftheweek = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)];
coffeeObj.isDirty = NO;
[appDelegate.coffeeArray addObject:coffeeObj];
}
}
}
else
sqlite3_close(database); //Even though the open call failed, close the database connection to release all the memory.
}
- (void) addCoffee1 {
if(addStmt == nil) {
const char *sql = "insert into Coffee(CoffeeName, dayoftheweek) Values(?, ?)";
if(sqlite3_prepare_v2(database, sql, -1, &addStmt, NULL) != SQLITE_OK)
NSAssert1(0, #"Error while creating add statement. '%s'", sqlite3_errmsg(database));
}
sqlite3_bind_text(addStmt, 1, [dayoftheweek UTF8String], -1, SQLITE_TRANSIENT);
if(SQLITE_DONE != sqlite3_step(addStmt))
NSAssert1(0, #"Error while inserting data. '%s'", sqlite3_errmsg(database));
else
//SQLite provides a method to get the last primary key inserted by using sqlite3_last_insert_rowid
LesID = sqlite3_last_insert_rowid(database);
//Reset the add statement.
sqlite3_reset(addStmt);
}
Insert:
coffeeObj.dayoftheweek = [NSString stringWithFormat:#"%#", dayoftheweek];
this insert: monday tuesday wednesday thursday friday saturday or sunday
But how can i display the data which is inserted in monday in the monday tableview and the data which is inserted in tuesday in the tuesday controller etc.
i tried ;
if([coffeeObj.dayoftheweek isEqualToString:#"Monday"]) {
cell.day.text = coffeeObj.LessonName;
} else {
}
Display:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CustomCellIdentifier = #"DaycusViewController";
DaycusViewController *cell = (DaycusViewController *)[tableView dequeueReusableCellWithIdentifier: CustomCellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"DaycusViewController"
owner:self options:nil];
for (id oneObject in nib) if ([oneObject isKindOfClass:[DaycusViewController class]])
cell = (DaycusViewController *)oneObject;
}
//Get the object from the array.
Coffee *coffeeObj = [appDelegate.coffeeArray objectAtIndex:indexPath.row];
cell.Name.text = CoffeeObj.CoffeeID;
cell.Day.text = CoffeeObj.dayoftheweek;
//i tried this: (not working)
/* begin */
if([CoffeeObj.dayoftheweek isEqualToString:#"Monday"]) {
// cell.Name.text = CoffeeObj.CoffeeID;
//cell.Day.text = CoffeeObj.dayoftheweek;
} else {
}
/* end */
//it need's to display in this example only things where dayoftheweek is monday but.
return cell;
}
call to function getInitialDataToDisplay
//Copy database to the user's phone if needed.
[self copyDatabaseIfNeeded];
//Initialize the coffee array.
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
self.coffeeArray = tempArray;
[tempArray release];
//Once the db is copied, get the initial data to display on the screen.
[Coffee getInitialDataToDisplay:[self getDBPath]];

It's hard to understand your question but i think you can better open a new project and start with Core Data. It's easy to understand and it's faster than SQLite.
Core Data is a framework Apple provides to developers that is described as a “schema-driven object graph management and persistence framework.” What does that actually mean? The framework manages where data is stored, how it is stored, data caching, and memory management. It was ported to the iPhone from Mac OS X with the 3.0 iPhone SDK release.
The Core Data API allows developers to create and use a relational database, perform record validation, and perform queries using SQL-less conditions. It essentially allows you to interact with SQLite in Objective-C and not have to worry about connections or managing the database schema
More about Core Data:
https://developer.apple.com/technologies/ios/data-management.html
https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/CoreData/cdProgrammingGuide.html
https://developer.apple.com/library/ios/#referencelibrary/GettingStarted/GettingStartedWithCoreData/_index.html
I wish you all the luck with your application, but i'm for sure that Core Data is the best for your application!

If I understand correctly your problem is not so much about SQLite but much more about how to wire up your view controllers correctly.
When the user selects one of the days in your first UITableViewController you have to pass the information about the selection on to the next view controller. Assuming you are not using storyboards it would probably look something like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:NO];
DaycusViewController *viewController = [[DaycusViewController alloc] initWithStyle:UITableViewStylePlain];
viewController.selectedDay = [days objectAtIndex:indexPath.row];
[[self navigationController] pushViewController:viewController animated:YES];
[viewController release];
}
As you now have the information available which coffees you want to display (e.g. the ones for "Monday") in your second view controller you can do for example what Diego suggested and filter your data accordingly.
Your current approach does probably not work because the UITableViewDataSource methods
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
and
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
depend on each other. So you have to be consistent here. The first one (...cellForRowAtIndexPath...) will be called for each row you want to display as you indicated in the second one (... numberOfRowsInSection ...).
So if you said you had 4 rows to display it will be called four times. What you cannot do is then use an if statement in the ...cellForRowAtIndexPath ... method to just return less than the four rows. Instead you should have a collection of the coffees for that specific day (ideally coming out of your data model ...). You can use the size of the collection for the ...numberOfRowsInSection... method and then return the corresponding element for each row using the index path in the ...cellForRowAtIndexPath ... method like this:
Coffee c = [coffees objectAtIndex: indexPath.row]
cell.name.text = c.name;
There are some quite good examples in Apple's documentation and some sample code that covers exactly your problem as well.

I'm by no means an expert of objective-c, but could this work?
// Pass dayoftheweek to the function
(void) getInitialDataToDisplay:(NSString *)dbPath:(NSString *)dayoftheweek {
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
const char *sql = "select coffeeID, coffeeName from coffee where dayoftheweek = ?";
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {
// Pass dayoftheweek to the query
sqlite3_bind_text(addStmt, 1, [dayoftheweek UTF8String], -1, SQLITE_TRANSIENT);
// Rest of the code, now the query will only return the data for the specified day

Related

Objective C: SQLite where-statement wont work when running another method first

So basically I have an app that will provide tasks based on selected project. Both projects and tasks are stored in a SQLite database.
To get the current project id I compare the selected project (_selectedProject) to my database, to get the ID. This is done in my getSelectedProjectId method. However, when running this method in the getTasks method, the Where-statement wont work at all. If I don't run the getSelectedProjectId method first, it works just fine. Am I forgetting to release something? Or is it something else? Any ideas?
I'm pretty new to both SQLite and Objective C, so this may not be a complex issue. I have made sure the getSelectedProjectId method returns the correct project ID. I have also made sure the query that is run in the getTasks method is correct, and when running it through my terminal it returns a number of rows. In the app it returns nothing, provided I'm running the getSelectedProjectId somewhere in that method first.
This is the method that fetches the tasks:
- (void)getTasks
{
[self openDB];
sqlite3_stmt *statement;
int projectId = [self getSelectedProjectId];
NSString *query = [NSString stringWithFormat:#"SELECT * FROM tasks WHERE project_id=%i", projectId];
const char *query_statement = [query UTF8String];
sqlite3_prepare_v2(_contactDB, query_statement, -1, &statement, NULL);
while (sqlite3_step(statement) == SQLITE_ROW)
{
// I add the task title to my array of tasks here.
}
sqlite3_finalize(statement);
sqlite3_close(_contactDB);
}
And this is the method that gets the correct project id from the database:
- (int)getSelectedProjectId
{
sqlite3_stmt *statement;
NSString *query = [[NSString alloc]
initWithFormat:#"SELECT id FROM projects WHERE title=\"%#\" LIMIT 0,1",
_selectedProject];
int rowId = 0;
const char *query_statement = [query UTF8String];
[self openDB];
sqlite3_prepare_v2(_contactDB, query_statement, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_ROW)
{
rowId = sqlite3_column_int(statement, 0);
}
sqlite3_finalize(statement);
sqlite3_close(_contactDB);
return rowId;
}
The problem occured because I closed the DB connection in my getSelectedProjectId-method. I'm now leaving my DB open instead, works like a charm.

How to display selected consumables in a table in Objective C

I am having an issue selecting appropriate consumables that I have set up in iTunes Connect to appear in my table in a nib file. (Using Ray Wenderlich's tutorial)
I have set up 11 consumables which are in two distinct sets; coins and categories.
What I want to do is have two buttons, each taking the user to a different table. Depending on the table a different set is displayed.
I have tried for hours to get this to work and I cannot do it; so I bow to the powers of the StackOverflow community to help.
Currently my set is as follows:
NSSet * productIdentifiers = [NSSet setWithObjects:
#"com.ac.scramble.coins1",
#"com.ac.scramble.coins2",
#"com.ac.scramble.coins3",
#"com.ac.scramble.coins4",
#"com.ac.scramble.coins5",
#"com.ac.scramble.category1",
#"com.ac.scramble.category3",
#"com.ac.scramble.category5",
#"com.ac.scramble.category8",
#"com.ac.scramble.category15",
#"com.ac.scramble.category30",
nil];
sharedInstance = [[self alloc] initWithProductIdentifiers:productIdentifiers
which is set up through iTunes Connect with no problem.
The evident problem here is that in my first nib which wants to show 6 rows, it is showing the final six in the list above (not sure why the final six rather than the first six...). This is fine, but in the second nib, which wants to show 5 rows, it is showing the final five from the list above. I don't want that - I want it to display the top five (the coins).
I've tried splitting the NSSet up into two and passing through but I cannot get that to work. I don't know if I can in the code somewhere specify which rows of the set I want to display. The pertinent code (I believe) which is probably where I need to do some trickery is:
- (void)reload {
_products = nil;
[self.table2 reloadData];
[[RageIAPHelper sharedInstance] requestProductsWithCompletionHandler:^(BOOL success, NSArray *products) {
if (success) {
_products = products;
[self.table2 reloadData];
}
//[self.refreshControl endRefreshing];
}];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"SimpleTableCell";
SimpleTableCell *cell = (SimpleTableCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"SimpleTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
SKProduct *product = (SKProduct *) _products[indexPath.row];
cell.nameLabel.text = product.localizedTitle;
NSLog(#"Localized title: %#", product.localizedTitle);
cell.thumbnailImageView.image = [UIImage imageNamed:[thumbnails3 objectAtIndex:indexPath.row]];
cell.descriptionLabel.text = [descriptions3 objectAtIndex:indexPath.row];
[_priceFormatter setLocale:product.priceLocale];
//cell.detailTextLabel.text = [_priceFormatter stringFromNumber:product.price];
cell.progressLabel.text = [_priceFormatter stringFromNumber:product.price];
cell.descriptionLabel.text = product.localizedDescription;
}
Thanks to all in advance.
I managed to work out the answer and it was indeed splitting the set up. This may not be the most efficient way of doing this but I took the Set of 11 elements and depending on which one I wanted I either used the removeLastObject or removeObjectAtIndex[0] methods to obtain what I wanted. Thanks to everyone's help!

NSInternalInconsistencyException with UITableViewController and a Storyboard-Pop

I integrated GrabKit in my iPhone-App, which is a Library to Grab Photos from social Networks. Now I have the following situation/problem:
I have a UITableViewController - AlbumListViewController.
Then there's a UITableViewCell - AlbumCellViewController.
When you tap on one of these cells - albums with photos
There's a UITableViewController - PhotoListViewController
and a UITableViewCell - PhotoCellViewController.
Everything here works just finde, I can browse albums, choose one, browse the photos in it and then when I choose one of the single photos, there is a PushViewController to my SetPhotoViewController, which displays the selected image in Fullscreen.
Now when I want to use the back-Button of my navigation bar in the SetPhotoViewController, the crashes with the following message:
*** Assertion failure in -[UISectionRowData refreshWithSection:tableView:tableViewRowData:], /SourceCache/UIKit_Sim/UIKit-2372/UITableViewRowData.m:400
2012-10-22 22:49:32.814 Amis[1820:c07] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Failed to allocate data stores for 1073741821 rows in section 1. Consider using fewer rows'
*** First throw call stack:
(0x23de012 0x1b63e7e 0x23dde78 0x15f9f35 0xc8f83b 0xc923c4 0xb56fa2 0xb5692c 0x1690f 0x1cd653f 0x1ce8014 0x1cd87d5 0x2384af5 0x2383f44 0x2383e1b 0x2a9a7e3 0x2a9a668 0xaab65c 0x42fd 0x2b75)
libc++abi.dylib: terminate called throwing an exception
DataSource and Delegate of my two UITableViewControllers are both set to File's Owner.
When I debug trough the code, I can't find any special things, all photos can be loaded, all the arrays are filled with data, there's nothing strange.
Here are the two functions of the PhotoListViewController which get called, as soon as I press the back button on my NavigationController:
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = nil;
NSLog(#"%i", indexPath.row);
NSLog(#"%ii", indexPath.section);
// Extra Cell
if ( indexPath.section > _lastLoadedPageIndex ){
static NSString *extraCellIdentifier = #"ExtraCell";
cell = [tableView dequeueReusableCellWithIdentifier:extraCellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:extraCellIdentifier];
}
cell.textLabel.text = #"load more";
cell.textLabel.font = [UIFont fontWithName:#"System" size:8];
} else {
static NSString *photoCellIdentifier = #"photoCell";
cell = [tableView dequeueReusableCellWithIdentifier:photoCellIdentifier];
if (cell == nil) {
cell = [[[NSBundle mainBundle] loadNibNamed:#"PhotoRowViewController" owner:self options:nil] objectAtIndex:0];
}
}
// setting of the cell is done in method [tableView:willDisplayCell:forRowAtIndexPath:]
return cell;
}
and..
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
// extra cell
if ( [cell.reuseIdentifier isEqualToString:#"ExtraCell"] ){
if ( state == GRKDemoPhotosListStateAllPhotosGrabbed ) {
cell.textLabel.text = [NSString stringWithFormat:#" %d photos", [[_album photos] count] ];
}else {
cell.textLabel.text = [NSString stringWithFormat:#"Loading page %d", _lastLoadedPageIndex+1];
[self fillAlbumWithMorePhotos];
}
} else // Photo cell
{
NSArray * photosAtIndexPath = [self photosForCellAtIndexPath:indexPath];
[(PhotoRowViewController*)cell setPhotos:photosAtIndexPath];
}
}
What am I missing?
Edit: The code of the numberOfRowsInSection-Method:
If I debug it, the first time res is equal 0, the second time the method gets called, res is equal 322121535.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSUInteger res = 0;
if ( state == GRKDemoPhotosListStateAllPhotosGrabbed && section == _lastLoadedPageIndex ) {
NSUInteger photosCount = [_album count];
// Number of cells with kNumberOfPhotosPerCell photos
NSUInteger numberOfCompleteCell = (photosCount - section*kNumberOfRowsPerSection*kNumberOfPhotosPerCell) / kNumberOfPhotosPerCell;
// The last cell can contain less than kNumberOfPhotosPerCell photos
NSUInteger thereIsALastCellWithLessThenFourPhotos = (photosCount % kNumberOfPhotosPerCell)?1:0;
// always add an extra cell
res = numberOfCompleteCell + thereIsALastCellWithLessThenFourPhotos +1 ;
} else if ( section > _lastLoadedPageIndex) {
// extra cell
res = 1;
} else res = kNumberOfRowsPerSection;
return res;
}
I'm the developer of GrabKit. Thanks for using it :)
Actually, the controllers in GrabKit are there for demonstration purpose only, they are not designed to be used "as is" in a project, and more specifically : The GRKDemoPhotosList controller was not made to have another controller pushed in the controllers hierarchy.
So what you need is just a little fix to make grabKit demo's controllers fit to your project :)
I guess the problem is the following :
_ in [GRKDemoPhotosList viewWillAppear], the method fillAlbumWithMorePhotos is called.
_ when you pop back from your controller to GRKDemoPhotosList, this method is called one more time, and it generates this bug.
Try to add a flag in the viewWillAppear method, to avoid calling fillAlbumWithMorePhotos twice, I guess it'll work fine.
Feel free to contact me by mail ( pierre.olivier.simonard at gmail.com ) or on twitter ( #pierrotsmnrd ) if you need more informations or help :)

Getting number of rows from SQLite C interface in Objective-C

I am new to objective-C and iphone apps.
I am accessing SQLite and have 3 rows in my table "coffee". I used the following way to grab sth out from the table, however, only then 2nd and 3rd rows are being pulled out {the 1st row is always missed}. Is that due to the logic in my while loop by checking while sqlite3_step(selectstmt) returns SQLITE_ROW is wrong? Here is the code:
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
const char *sql = "select coffeeID, coffeeName from coffee";
sqlite3_stmt *selectstmt;
NSLog(#"sqlite_prepare_v2 returns: %i", sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL));
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {
NSLog(#"sqlite3_step returns: %i", sqlite3_step(selectstmt));
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
NSInteger primaryKey = sqlite3_column_int(selectstmt, 0);
Coffee *coffeeObj = [[Coffee alloc] initWithPrimaryKey:primaryKey];
coffeeObj.coffeeName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)];
NSLog(#"this is the coffee name: %#", coffeeObj.coffeeName);
coffeeObj.isDirty = NO;
[appDelegate.coffeeArray addObject:coffeeObj];
[coffeeObj release];
}
}
}
On the other hand, is there any convenient way for me to check the number of rows returen in a query directly from the C interface of SQLite?
Many thanks.
You could use the query SELECT COUNT(*) FROM coffee to tell you how many rows there are.
And also, save yourself some headaches and use a SQLite wrapper.
Are the 2 sqlite3_step() calls meant to be executed here?
NSLog(#"sqlite3_step returns: %i", sqlite3_step(selectstmt));
while(sqlite3_step(selectstmt) == SQLITE_ROW {
BTW: there a parenthesis missing in the while line. Do not rewrite your code for SO. Copy/Paste it to avoid copying errors (pasting errors are much more rare)

Performance when building the Objective C application in the device

I have a performance problem when I build the application in the Device. It is actually in my database. I have a table of wine details in which there are 2114 wine names. To get the all those wine names, I wrote this code in the appDelegate:
-(NSMutableArray*)getWineDetails
{
[wineDetailsList removeAllObjects];
sqlite3_stmt* statement;
const char *sql = "select *from wineDetails order by name";
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) != SQLITE_OK)
{
NSAssert1(0, #"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
}
while (sqlite3_step(statement) == SQLITE_ROW)
{
primaryKey = sqlite3_column_int(statement, 0);
//printf("\n primaryKey1 Value:%d",primaryKey);
wineDetails *wineDets = [[wineDetails alloc] initWithPrimaryKey:primaryKey database:database];
[wineDetailsList addObject:wineDets];
//printf("\n ==========================%d",[wineDetailsList count]);
[wineDets release];
}
sqlite3_finalize(statement);
printf("\n Inside AppDelegate .....wineDetailsList count:%d",[wineDetailsList count]);
return wineDetailsList;
}
I am calling this method in the viewWillAppear of another controller where I have to display the wine names in the table view.
The viewWillAppear code:
-(void)viewWillAppear:(BOOL)animated
{
CorkItAppDelegate* appDelegate = (CorkItAppDelegate*)[[UIApplication sharedApplication] delegate];
winesList = [appDelegate getWineDetails];
[tableView reloadData];
}
Here the problem is that when I build it in the device, it takes too much time to navigate into the controller due to the amount of date in the database. What should I do to get rid of this performance issue?
Thanks,
Monish Kumar.
Just as a quick suggestion, you could add an index on the name column, that might speed up the fetch. Also, make sure you're not fetching any more things than you need.