Debug iOS applications in six easy steps

In this tutorial, we’ll debug an iOS application with Apple’s Xcode. Xcode is a robust environment for developing and troubleshooting iOS applications. We’ll see how we can use it, alongside Raygun’s iOS Crash Reporting, to quickly address an application deficiency.

We’ll follow six steps to examine a simple application and isolate and fix a bug.

  1. Setup a sample project
  2. Analyze a Raygun debug report
  3. Explore Xcode’s debugging tools for iOS
  4. Set a breakpoint in Xcode
  5. Run the application with a breakpoint
  6. Isolate the bug and fix it

This tutorial requires a macOS computer with Xcode installed. We’ll be using the current version of Xcode at the time of this writing, which is version 10. The source code for this project is available on Github.

So, let’s get started!

Step 1: Sample project

We’ll use a single view iOS application for this tutorial. The single view has a button with text that changes for each press. If you want to follow along, you’ll need to sign up for a Raygun account. After you check out the project, add your Raygun API (Application Programming Interface) key to didFinishLaunchingWithOptions in AppDelegate.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    [[Raygun sharedReporterWithApiKey:@"YOUR_RAYGUN_KEY"] attachPulse];
    return YES;
}

You can find complete instructions for setting up an iOS application. You can follow this tutorial with your application if you prefer.

Here is the sample application’s startup view.

Debug iOS: Image of the sample app view

Each time you tap the button, the display text changes. After a few presses, the application crashes. Imagine that this application has been released to the App Store and was already approved. You need to track down the bug fast!

Step 2: Analyze the Raygun debug report {analyze}

Fortunately, you integrated your application with Raygun Error Monitoring and Crash Reporting, so you can use the information to get an idea of where to look for the bug.

First, let’s look at the error summary.

Debug iOS: Image of the error instance report in Raygun

Since we forced the error now, we don’t need the date and time. But in the event of any errors happening in deployed applications, the date and time can be advantageous. The class name and error message tell us a lot about the error. NSRangeException indicates that the code exceeded the bounds of a data structure, and the report suggests a subscript out of range is causing the crash. Raygun also provides a backtrace for the error.

Debug iOS: Image of the backtrace in Raygun

Since we’re dealing with an uncaught exception, the backtrace only provides us with a rough idea of where the error is occurring; somewhere in UIKit. So, we’ll need to run the application in a debugger to find the exact location.

Step 3: iOS debugging with Xcode

Xcode provides iOS developers with an integrated environment for developing, testing, and debugging iOS applications. The simulator supports all current iOS platforms. You can install and run applications with a single click, and the environment has integrated support for the lldb debugger.

Debug iOS: Image of the lld debugger

So, you can debug your application on your development desktop. Let’s start with replicating the error in the simulator.

Debug iOS: Image of how to replicate the error in the simulator

We want to be sure we run a debug build, so start by holding the option key and clicking the run icon on the upper left-hand side of Xcode. This brings up the run options dialog.

Make sure that you have Run selected on the left-hand side, and debug build enabled on the right. Now, click on the run button. After the build completes, the application will start in the simulator.

Debug iOS: Image of the app starting in the simulator

Next, click the iOS app’s button until the application throws the exception and the debugger comes up in Xcode.

Debug iOS: Image of the debugger in XCode

On the left-hand side, the debug navigator displays the application threads. Xcode highlights main in thread #1 since uncaught exceptions “bubble up” to that location in the application. In the editor pane, Xcode highlights that function in main.m. Xcode shows us that a SIGABRT was thrown inside main. Down at the bottom of Xcode lldb’s command line interface is available. We’re going to control it from the GUI (Graphical User Interface).

Finally, click the stop button, next to run, to stop the debug session. Raygun already told us that the application has an uncaught exception, so we haven’t learned anything new about the bug yet. Now, let’s use Xcode to catch the exception and discover where it’s coming from.

Step 4: Set exception breakpoint

So, go to the Debug menu, select Breakpoints and Create Exception Breakpoint.

Debug iOS: Image of the debugger in XCode

Xcode brings up the breakpoint navigator on the left, and a popup will appear.

Debug iOS: Image of the debugger in XCode

The default is to break on both C++ and Objective C exceptions and to stop the debugger where they are thrown. This will take us to where the problem occurs. Next, click anywhere to dismiss the popup and accept the defaults.

Step 5: Run the application with breakpoint

Now, rerun the application and force the error.

Debug iOS: Image of the debugger in XCode

The debugger stopped when the application generated the exception and highlighted the line of code that is responsible.

Debug iOS: Image of the debugger in XCode

Just as you might expect from the Raygun error report, the code references an array. The problem is clear from viewing the highlighted line. We’re incrementing the array index without checking bounds. If you look down in the debug window in Xcode, you can see that counter somehow made it to a value of five when the array is only four elements long.

The application should have crashed sooner than it did.

But what if the problem was more difficult to isolate? Let’s add one more breakpoint and step through the application again.

Step 6: Isolate the bug and fix it

First, stop the debug session

Next, double-click next to the offending line in the source code to bring up a breakpoint dialog popup.

Debug iOS: Image of the debugger in XCode

Add a condition for the breakpoint; counter > 3.

Rerun the application, and when you force the error, it will stop before iOS throws the exception. The array index and the increment are on the same line of code. So, continuing after the breakpoint will still result in iOS throwing the exception. This breakpoint makes the bug clear, and we can see how conditional breaks can help isolate a thorny problem.

There are several ways to fix this bug. Most of the better ones involve a different design, but let’s go for a quick fix.

@implementation ViewController

int counter = 0;

NSArray *labels = nil;
+ (void)initialize {
    labels = @[@"Ow!", @"Ouch!", @"Oof!", @"Stop!"];
}

- (IBAction)boomButton:(id)sender {

    if (counter == 3) {
        counter = 0;
    } else {
        ++counter;
    }

    [sender setTitle:[labels objectAtIndex: counter] forState:UIControlStateNormal];

}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

@end

By checking the array bounds before we increment the counter, we never exceed the array length. Rerun the application, and the crash is gone.

Next steps

Apple’s XCode development tool simplifies debugging iOS applications. It provides integrated support for an advanced debugger as well as GUI-based shortcuts for common tasks like adding breakpoints.

Fortunately, you can also rely on their simulator to accurately emulate the full set of iOS devices since Apple controls the platform. Once you’ve verified an issue is addressed in the simulator, you can push to local devices and test there too.

Of course, with Raygun, you have an extra layer of protection for detecting, diagnosing, and eliminate iOS errors. The Raygun report in this example told us what the failure was, and made it much easier to isolate. Raygun’s smart alerts and error grouping means you never miss a bug in your app again. Read more about iOS crash reporting