iOS Development Best Practices

There are a lot of ways to successfully develop an iOS application from the ground up. Working in a team can be difficult, especially if everyone has a different idea of how to accomplish the tasks at hand. Here are some general best practices for developing an iOS app.

Register for GitHub.

Using version control of some kind is not optional. I suggest using GIT with GitHub as it adds several tools to help with collaboration such as a news feed of activity, wiki and simple bug tracker.

Define some SCM best practices.

First, start your project off on the right foot with a proper git ignore file, which GitHub provides here. Next set some guidelines for commit messages. Since you're using a bug tracker (see below), define how commits that fix bugs should be expressed. Here's an example of a good commit message.

Fix for interaction being disabled for an entire view hierarchy on iOS 4.X whereas iOS 5.0 only disables interaction on the view being animated. Now the cancel button is tappable on iOS 4.X. Fixes #243.

Now here's a bad commit message.

Fix for #243.

Make sure your team knows that you need a detailed commit message and a reference to the bug number using a unified format. Some bug trackers can parse bug numbers and assign commits to the actual bug, making a descriptive commit even more important.

Set up a bug tracker.

This part might seem optional or unnecessary on small projects. It's not. Having a bug tracker helps keep your developers focused on remaining tasks and helps management define releases.

At the very least, use GitHub Issues. But consider something a little more full-featured such as JIRA.

It's a good idea to set up best practices for your bug tracker too. Here are a couple of my personal best practices.

Everything goes in the bug tracker. No matter how trivial or silly a bug is, put it in the bug tracker. Don't mention it to a developer over lunch and think that it will get fixed. It might, but it probably won't.

Don't mix up severity and priority and use both. Severity is how severe a bug is. Does it cause a crash? Does it delete user data? Does it set the user's house on fire? Priority is how important a bug is to finish. This can (and should) factor in severity, but all Severity 1 bugs aren't necessarily the highest priority. If you have a crasher, but you can only reproduce by doing 16 tasks in a particular order, it's probably not the highest priority. Conversely, a typo would be of the lowest severity. But if it's your product name on your splash screen, that should be high priority.

Use the features of the tracker. Add a build number to your bugs. Make sure that your bugs have target versions associated with them. Attach screenshots and include steps to reproduce. These all might seem obvious, but you'd be surprised how much time is wasted working off of incomplete bugs.

Use assignment and bug state in a structured way. When a bug first gets put into the system, it gets assigned to a QA manager. QA then verifies the bug exists and cleans up the bug description and steps to reproduce. After verification, QA assigns it to an engineer. When the engineer is done, the bug is marked as fixed (not closed) and assigned back to QA. When the bug is verified fixed, it is closed.

Register for TestFlight.

TestFlight allows you to distribute iOS builds over the air to groups of testers. It's magic. TestFlight also supports distribution groups. Meaning you can choose where your builds go. Set up a "Continuous" distribution group for the automated builds. It should only include your development team and perhaps your QA. These builds are bleeding edge, so sometimes they are bug-filled.

Adding new testers to TestFlight is a breeze. Once a tester registers for TestFlight and installs a simple configuration profile, you immediately have access to her device identifier. So your new tester process will go something like this:

  1. Invite them to your team on TestFlight.
  2. Put them in a distribution group that makes sense.
  3. Add the device to your provisioning portal.
  4. Generate a new provisioning profile.
  5. Add the profile to the build machine (see below).

Setup a build machine.

Your build machine sits there, listens for updates on GitHub, builds the latest source tree and pushes the new build to TestFlight. It is the hub that stitches all these pieces together.

Continuous integration is a valuable tool for development. Being able to discover bugs as soon as they develop is required for a quality software product. If bugs are only caught near the end of a development cycle, be prepared to blow right past the due date.

Several tools can help with this battle against bugs. Jenkins is a continuous integration tool that can kick off builds as soon as commits hit your repository. Tests can be run as part of the build and developers can be held personally responsible for failures introduced.

After a successful build, TestFlight can be used to push new builds directly to your testers. Automated, bleeding edge builds can be sent to your internal QA, and select builds can be promoted to your clients or external beta testers.

I won't go into details, but if you'd like a good tutorial on setting up Jenkins + TestFlight, check out this tutorial.


Ah, the hard part. Definitely integrate the TestFlight SDK into your app. It brings a couple cool features to the table:

  • Crash reporting. This is the killer feature. TestFlight will collate and symbolicate your crash logs for you.
  • The TFLog() calls for the sessions. In development, just define NSLog as TFLog.
  • A list of activity in your application. By using "checkpoints" you can see when users are doing particular tasks.
  • Update notifications. When your build server kicks out a new build, users of the old build will get an in-app notification of the build.

So that's a high-level overview of best practices when developing a project on iOS. I use these steps even when I'm working by myself. When working in a small or large team, being able during the debug cycle to iterate on a bug and get a build to the testers within minutes, is incredibly valuable. Once you work on a project like this, going back to ad-hoc builds and disorganized bug reporting will be unthinkable.