Finding iOS 5 bugs is like playing spot the difference

A bug report is filed. It has these ominous words: iOS 5 bug. Dun-dun-duuun. What do you do?

Twice now, this has happened to me at work. Here’s a technique I’ve used to find and fix the problems. It’s served me well!

First, fire up Xcode 4 and see if you can reproduce the iOS 5 bug in the iPhone 5.0 Simulator (or the iPad 5.0 Simulator, depending on your app). If you can get it to happen in the simulator, it makes life that much easier, because we’ll be switching back-and-forth between iOS versions.

Now rerun the scenario in the iOS 4.3 Simulator. Can you verify that the bug doesn’t occur there? Then we’re in business. You have a genuine iOS 5 bug!

Finding iOS 5 bugs: Where do we even start?

What could be causing the problem? Where do we begin to diagnose an iOS 5 bug?

First, don’t even bother reading through your code to look for anything suspicious. You won’t find anything. After all, you verified that the problem doesn’t occur on an older version of iOS. Under certain circumstances, you can say, “This code works.” There isn’t anything obviously wrong. So what’s going on?

Remember that we’re writing against an elaborate framework, relying on iOS to invoke our methods. With each new release of iOS, Apple isn’t just adding features; they’re also trying to improve battery life. If they find any call sequences they can eliminate, they’ll do it. But sometimes, this means an inefficiency you used to rely on may have been removed, causing one of your methods to no longer be invoked, or invoked in a slightly different order.

Method logging to the rescue

So now your job is to find what’s changed in the way your methods are invoked. To do this, we’re going to add method logging. This allows us to determine the order of method calls.

Go to the view controller in question. At the beginning of each method, add this statement:

printf("%s\n", __PRETTY_FUNCTION__);

Don’t do this manually if you can help it! If you use the following style of braces and indentation, you’re in luck:

- (void)foo
{
    // code here
}

Find and replace all occurrences of newline, open brace, newline, and whatever you use to indent your code (spaces or a tab). You want to end up with

- (void)foo
{
    printf("%s\n", __PRETTY_FUNCTION__);
    // code here
}

Compile to see if you’ve introduced the printf anywhere that doesn’t make sense (like a struct) and get rid of the those.

If you use K&R-style braces like

- (void)foo {
    // code here
}

you’ll have to be a bit more clever, because we only want the opening braces of methods, not of any other block statements. If you use this brace style with tab indents, you can use regular expression find-and-replace: search for ‘ {\r\t(\w)’ and replace it with ‘ {\r\tprintf("%s\n", __PRETTY_FUNCTION__);\r\t\1’. Note the leading spaces. Here’s how it looks in BBEdit:

BBEdit regular expression

Spot the difference!

So what does that odd-looking statement do? And why printf, why not NSLog?

__PRETTY_FUNCTION__ is a special gcc identifier for the name of the current function. The function name is formatted to match Objective-C (or C++) syntax. Here’s the kind of output you should get:

-[MyViewController viewDidLoad]
-[MyViewController viewDidAppear:]
-[MyViewController viewWillAppear:]

What we want is to run once on iOS 4, logging the calls. Then we run again on iOS 5, and compare the results. By using printf instead of NSLog, we avoid the timestamps that NSLog outputs. We want to make the two logs as similar as possible, in order to bring out the differences.

Are you ready? Let’s do it!

  • Turn off any other logging you use.
  • Run your scenario on the iOS 4 Simulator. Copy the log to a new document, and clean it up so it only shows the method calls.
  • Re-run your scenario on the iOS 5 Simulator. Copy the log to a new document, and clean it up so it only shows the method calls.
  • Compare the two and find the differences. I like using BBEdit, which has a handy Search menu item “Compare Two Front Documents”.

Somewhere in the differences, there’s a method that’s called on iOS 4 but not on iOS 5. You’ll probably notice several differences, so you’ll need to do some sleuthing to identify which one causes the iOS 5 bug. Here are some things to try:

  • Narrow the list of differences down to the likely candidates related to the functionality that’s missing.
  • Try setting breakpoints on the candidate methods, and run your scenario on iOS 4.
  • When you stop at breakpoints, note the call stack.
  • Set breakpoints further back on the call stack, and step through on iOS 5.

Be patient: You have two lists of method calls and clear differences between them. It’s in there somewhere. Once you find what’s not being invoked on iOS 5, add a direct call to it.

If  you can confirm that this changes fixes the iOS 5 bug, make sure it doesn’t introduce a different problem on iOS 4. (Don’t forget to clean out the printf statements!)

Good luck, and happy hunting. If this helps you track down an iOS 5 bug, let us know in the comments below!

Photo by Kaustav Bhattacharya (license)

About the Author: Jon Reid

Jon Reid writes Quality Coding, a blog exploring tools, tips & techniques for building quality in to iOS development. Jon is especially passionate about unit testing and test-driven development. You can follow Jon on Twitter and on Facebook.