iOS Quick Tips: Why can’t I compare two numbers?!?

Recently, I was trying to debug some code that wasn’t working properly. The code looked something like this:

if(someNumber > 0)
{
   // Do some really important stuff
}

… but even when someNumber was supposed to be 0, the block was always being entered. What the heck? I can’t even compare two numbers properly? I smacked my forehead when I realized that someNumber was actually a NSNumber*, not an int, and what I really wanted to do was this:

if(someNumber.intValue > 0)
{
   // Do some really important stuff
}

Of course, this wasn’t flagged as a warning by the compiler, because as is typical with the C family of languages, checking if a pointer (or an id) is greater than 0 is a perfectly valid operation.

I guess the moral of the story is that if some basic operation isn’t working, take a step back and make sure that your initial assumptions are valid!

iOS Pain Points: Autocomplete in Xcode is a usability disaster

Xcode, like many IDEs, gets autocomplete wrong. Compared to Visual Studio 2010, for example, autocomplete in Xcode is a frustrating experience. Here are some of the ways it is inadequate:

  • It commits the cardinal sin of autocompletion by requiring that you press enter or tab to accept an autocompletion. It should accept the current selection with any key press that makes contextual sense – such as ], ;, =, or (, instead of only accepting the current selection when you press enter or tab.

This is a pretty subtle point, but it makes a world of difference in terms of usability. Here’s an example. Suppose you want to type

self.foo = bar;

In Xcode, i have to do the following:

s[enter].f[enter][space]=[space]b[enter];

for a total of 11 keypresses.
To get the same thing in VS 2010, using C#, you have to type the following

t.f=b;

.. for a total of 6 keypresses – almost half as many as the Xcode version. And it even automatically inserts spaces around the = sign.

  • As previously discussed, it does a horrible job of offering context-sensitive help when for constants or enums, populating the list with everything in the current scope, regardless of how irrelevant it is or how many compile errors using it would cause.
  • It does not display the autocomplete list automatically when you press period. Or, for that matter, when sending a message with [foo . You have to either start typing (which isn’t very useful if you don’t know what you are looking for), or press Esc (which is a pain).
  • When you use autocomplete to insert a function call such as CGRectMake(,,,), Xcode inserts the last ), but there seems to be no way to have it automatically insert the ; or move the cursor outside of the ). This means that you have to take your hands off the keyboard and use the arrow keys to manually move the cursor over just so you can type a stupid semicolon.
  • Pressing PageUp or PageDown doesn’t scroll the autocomplete list, it hides the autocomplete list and scrolls the file you are editing.

It’s death by 1000 paper cuts, and it makes iOS development far more painful than it needs to be.

Anybody have any autocomplete frustrations that I missed?

iOS Quick Tips: Version control and backups with DropBox and Git

Git is an amazing VCS. However, Git by itself is not a backup if you are only using it locally – if your hard drive dies, so does your data. Fortunately, Git’s flexibility lets us do some really cool things, such as push and pull from a ‘remote’ repo on the same machine. When combined with a distributed storage system such as Dropbox, it becomes the perfect tool for the solo developer.

Here’s how you go about using Dropbox with Git. This little tutorial assumes that you already have a local Git repository (perhaps created by Xcode).

Step 1 is to install Dropbox on your Mac. After the install is complete, you’ll notice that you now have a Dropbox folder in your home directory.

Everything you put in this folder will be magically backed up in the Dropbox cloud.

Step 2 is to create a bare Git repository for your project in the Dropbox folder. You can do this from Terminal with the following command, after CD-ing into the Dropbox folder:

git init –bare MyProject.git

This creates the Git repository that your work will be pushed to.

Step 3 is to link your existing Git project with your new Dropbox’d Git repository. After CD-ing into the directory containing your project, issue the following command to add your new Dropbox repository as a ‘remote’ for your project:

git remote add origin ~/Dropbox/MyProject.git

Step 4 is to push your changes to your new ‘remote’ repository:

git push origin master

That’s it! Your local Git repository is now synced with your Dropbox folder. Not only does this give you a safe, distributed backup, but it makes it easy to work on your projects from any machine.

Happy developing!

iOS App Postmortems: PhoToDo

February’s app – PhoToDo – is now available on the App Store, and just like last month, here is the postmortem for it.

Development Time:

  • ~23 Hours or so, over a period of about three weeks.

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

  • 8 days the first time. Rejected due to a crash when running on the iPad.
  • 5 days the second time.

New technologies I learned and used:

  • Sqlite3
  • UIActionSheets
  • UIPickerViews
  • UITabBars
  • UIImagePickerControllers

Challenges:

  • Designing the details screens to fit everything I wanted. I’m still not thrilled with the result, but it will have to do for now. This challenge will only get larger as I add more features to the application.
  • Overcoming some hurdles with UITableViews, and trying to figure out how to bend static cells to my will.
  • The App Store review process and waiting game. This will likely be the target of a blog post in the near future. On the bright side, I learned an important lesson – always test your app on the iPod simulator as well as the iPhone simulator!

Future plans: Version 1.1 is already in development. Here are some of the things I would like to see in 1.1 and beyond:

  • Support for reminder notifications for tasks with a due date
  • The ability to search your tasks & notes
  • The ability to add new tasks and notes directly from the task/note details screen
  • More photo editing/manipulation tools

As always, please let me know if you have any feature requests or ideas!

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