iOS Quick Tip – programmatically hiding sections of a UITableView with static cells

As we have seen previously, getting a UITableView with static cells to show up can be a bit tricky. You need to remove some of the table methods. However, if you want to dynamically show and hide sections at runtime, you will need add one of them back in and put in a little bit of extra effort.

We’ll start out with a static UITableView that looks like this:

We’ll then add actions for the button touch events, which will set a flag indicating whether or not to hide the middle section:

- (IBAction)HideButtonTouch:(id)sender
{
    hideTableSection = YES;
    [self.tableView reloadData];
}

- (IBAction)ShowButtonTouch:(id)sender
{
    hideTableSection = NO;
    [self.tableView reloadData];
}

We then have to re-implement our previously removed method tableView:numberOfRowsInSection to return the correct number based on whether or not the middle section should be shown:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if(section == 1)
    {
        if(hideTableSection)
            return 0;
        else
            return 1;
    }
    else
    {
        return 2;
    }
}

This gets us closer, but the section header is still showing:

The next thing we have to do is make sure the header doesn’t show up. We do it by overriding theset two methods. Note that we are returning 1 in heightForHeaderInSection – if we return 0, it won’t work.

-(UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    if(hideTableSection && section == 1)
        return [[UIView alloc] initWithFrame:CGRectZero];
    return nil;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    if(section == 1 && hideTableSection)
        return 1;
    return 32;
}

This gets us most of the way there, but it looks like the gap in between the two sections is a little too large. We can get around this by messing with the size of the footer in a similar manner:

-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
    if(section == 1 && hideTableSection)
        return 1;
    return 16;
}

-(UIView*)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
    if(section == 1 && hideTableSection)
        return [[UIView alloc] initWithFrame:CGRectZero];
    return nil;
}

Voila! Our section is now properly hidden.

I’m sure this isn’t the ideal way to show and hide sections, but it works well enough for my purposes. And since it took a little bit of effort to figure out, I wanted to share it. Enjoy!

Download the Demo Project

iOS Quick Tips: Avoiding SQL injection attacks with parameterized queries in sqlite3

Since my current app requires a sqlite3 database, I naturally hit up Google for some sqlite3 tutorials. I found plenty of tutorials, but sadly, most of them were poorly written and full of dangerous code. The problem? SQL injection.

Most of the tutorials I saw had code that looked something like this:

NSString* unsafeSqlString = [NSString stringWithFormat:
     @"UPDATE record SET name=\"%@\" WHERE id=%@", record.name, record.id];

What’s the problem here? Good ol’ Bobby Tables. Depending on what record.name is, your SQL statement may do something that you weren’t expecting (you didn’t want to delete all the records in your table? Should’ve thought of that before writing such unsafe code!).

The correct way to do this is with parameterized queries. In sqlite3, you use parameterized queries by replacing your values with ?’s in your SQL statement. So the above unsafe SQL statement becomes this:

NSString* safeSqlString = @"UPDATE record SET name=? where id=?";

You then prepare a SQL statement with your query:

sqlite3_stmt* statement;
if(sqlite3_prepare(database, [safeSqlString UTF8String], -1, &statement, NULL) != SQLITE_OK)
{
  // Add your error handling code here
}

Finally, you bind all of your parameters to set the actual values that will be used when your query is executed:

if(sqlite3_bind_text(statement, 1, [record.name UTF8String], -1, SQLITE_TRANSIENT) != SQLITE_OK)
{
  // Add your error handling code here
}
if(sqlite3_bind_int(statement, 2, record.key) != SQLITE_OK)
{
  // Add your error handling code here
}

For more details on all the different sqlite3_bind functions, see the SQLite documentation.

Finally, you execute your safe, injection-free SQL statement:

if (sqlite3_step(statement) != SQLITE_DONE)
{
  // Add your error handling code here
}

Sure, avoiding SQL injection attacks in a small iOS app is probably less of an issue than avoiding them in a mission-critical website, but that’s no excuse to do it wrong. And here’s another, even more obvious reason to do it right: If any of your fields have a ” in them, your unsafe SQL statement will fail. The parameterized statement handles quotes correctly.

iOS App Postmortem: BBQ Menu

Now that January’s app – BBQ Menu – is available on the App Store, I thought I would do a quick postmortem for it. Postmortems will likely become a regular feature of this blog, and I suspect that they will normally be a little bigger than this one.

Development Time: ~15 Hours. This is a pretty rough estimate as I wasn’t keeping track of time. I have since started using Thyme to track my hours.

Review Time (time spent in the iOS app submission queue): 7 days

New technologies used: Everything! As this was my first app, everything was new, from UITableViews to Xcode to distribution and deployment.

Challenges: Aside from getting acclimated to an entirely new set of development tools, the hardest part was figuring out where to draw a line in terms of features. It’s a delicate balance between the desire for more features, and the desire for a simple, easy to use application.

Future plans: There are a number of bug fixes coming in the immediate future, and possibly some new features and a prettier UI shortly thereafter. If you have any feedback regarding BBQ Menu, please drop me a line!

iOS Quick Tip – UITableView with static cells not showing up

If you’ve ever tried to create a UITableView with static cells, you may have run across some difficulties in getting your cells to actually show up.

The trick is to remove the following row, cell, and section methods from your UITableViewController subclass. They are automatically added by Xcode, but if you are using static cells, you don’t want them in your class.

– (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

– (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

– (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

Once you have removed these methods, your cells will show up correctly.

January’s Goal – Complete

Sort of.

I submitted January’s app to the app store on the 30th, but it’s still waiting for review. So although it’s not technically in the app store yet, I consider this a pretty successful month. And I’m already hard at work on February’s app – this time using Thyme to keep track of exactly how long it takes to build.

I’ll put up more info on each of the apps as they are released