Debugging Applications With Xcode

Symbolic Breakpoints

Earlier in this series, I briefly listed the types of breakpoints you can use in Xcode. In the previous episode, we focused on file and line breakpoints. This episode zooms in on symbolic breakpoints.

Symbolic Breakpoints

Remember that a file and line breakpoint is associated with a specific location in the codebase hence the name. A symbolic breakpoint is different. As the name implies, a symbolic breakpoint is linked to a symbol, such as a method or function name. This type of breakpoint can be useful for debugging problems that involve code you don't have direct access to.

Symbolic breakpoints are often used in scenarios in which your code interacts with a third party library or framework, including Apple's libraries and frameworks. Because you don't have access to the source code of the library or framework, it isn't possible to define a file and line breakpoint. Let me show you how symbolic breakpoints work and why they're useful.

Example 1

Let's revisit Cloudy. Add a file and line breakpoint to RootViewController.swift on line 144, the first line of the fetchWeatherData() method. If we run the application, the debugger pauses its execution on line 144 of RootViewController.swift. This should be familiar by now.

File and Line Breakpoints

Let's replace the file and line breakpoint with a symbolic breakpoint. Disable the file and line breakpoint. Click the + button in the bottom left of the Breakpoint Navigator and choose Symbolic Breakpoint.

Adding a Symbolic Breakpoint

A symbolic breakpoint is linked to a symbol, such as a method or function name, and Xcode asks us for the symbol the debugger needs to break on. Don't let yourself be thrown off by the term symbol. A symbol is nothing more than an identifier that uniquely identifies a method or function name. It's a bit more complex under the hood, but that's what you need to know to work with symbolic breakpoints.

Adding a Symbolic Breakpoint

If we want to break on the fetchWeatherData() method, we enter the name of the method as the symbol of the breakpoint. Xcode offers us suggestions from the moment we start typing.

Adding a Symbolic Breakpoint

Notice that we don't append a pair of parentheses to the symbol. The debugger is only interested in the name of the method or function. We leave the other fields empty for now.

Adding a Symbolic Breakpoint

Run the application to see the result. A few interesting things happen. The debugger pauses the application on line 144 in RootViewController.swift. The application is paused every time the fetchWeatherData() method is invoked. More interesting is what we see in the Breakpoint Navigator. Xcode shows us the matches it has found for the symbolic breakpoint.

Adding a Symbolic Breakpoint

This may not seem useful, but let me show you what happens if we subclass the RootViewController class and override the fetchWeatherData() method. Create a new file, choose the Cocoa Touch Class template, and name the class NewRootViewController. Make sure it inherits from the RootViewController class.

Create a New File

Create a New Class

Remove the private keyword of the fetchWeatherData() method in the RootViewController class.

func fetchWeatherData() {
    ...
}

Override the fetchWeatherData() method in the NewRootViewController class.

import UIKit

class NewRootViewController: RootViewController {

    override func fetchWeatherData() {

    }

}

Run the application. Before Xcode launches the application, it looks for matches for the symbolic breakpoint we defined and updates the list of matches. That's the power of symbolic breakpoints. Let's look at another example.

Xcode updates the list of matches.

Example 2

In the first example, we replaced a file and line breakpoint with a symbolic breakpoint. While this can be useful at times, symbolic breakpoints are very often used to break on methods or functions you don't have direct access to. Let's see how that works in the second example.

Disable the symbolic breakpoint we defined earlier and add a new symbolic breakpoint. Set the symbol of the breakpoint to layoutSubviews, a method of the UIView class defined in the UIKit framework.

Adding a Symbolic Breakpoint for layoutSubviews

Run the application to see what happens. Xcode looks through the codebase and shows us a list of matches for the symbol. Notice that Xcode also lists matches in libraries and frameworks the project is linked against. It's a pretty long list, which ins't surprising since many UIView subclasses override the layoutSubviews() method.

Xcode shows us a list of matches.

The debugger pauses the application and the stack trace in the Debug Navigator shows us why. The stack trace isn't very useful, but it shows that the debugger paused the application on the layoutSubviews symbol.

If we take a closer look at the matches in the Breakpoint Navigator, we see that Xcode shows us the module in which the match was found. Most of the matches were found in UIKit, but we also see MapKit, ContactsUI, and AppSupportUI.

Xcode shows the module in which the match was found.

This is useful because we can ask the debugger to only pause the application if layoutSubviews() is invoked by a class of, for example, the UIKit framework. To accomplish that, we edit the breakpoint and specify UIKit in the module field.

We edit the breakpoint and specify UIKit in the module field.

Before Xcode launches the application, it updates the list of matches. It can sometimes take a few seconds.

Breaking on layoutSubviews() isn't very useful because it's invoked very frequently, but the example shows the power of symbolic breakpoints. Later in this series, we use symbolic breakpoints to debug Auto Layout issues.

What's Next?

In the next episode, we look at the Swift error and exception breakpoints. These breakpoints are very useful to debug problems that involve errors or exceptions.

Next Episode "Swift Error and Exception Breakpoints"

Stop Writing Swift That Sucks

Download the Swift Patterns I Swear By