Breakpoints

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

In the previous sections, you learned how to identify and eliminate potential syntax bugs in code. Now, you’ll learn about some of the tools Xcode provides to monitor your code as it’s running. With syntax bugs mostly eliminated before compiling, you’ll tackle bugs in logic next.

The primary tool you’ll use is setting breakpoints in your code. When the system encounters a breakpoint, it sends a halt message to every thread of your app. When it gets the halt, each thread should stop and record its call stack. The call stack is a list of every part of the code from the start of a thread until the current line being executed.

With uncomplicated apps, there’s only one thread you need to consider. However, several other threads will run simultaneously as the system interacts with your app. The thread where most of your code runs is the main thread.

After the threads have all halted, Xcode will show you the code that was just about to execute. It’ll also show you the current value of all variables and the stack trace of the threads.

To set a simple breakpoint, click the line number in the code where you’d like to set it.

Xcode's Debug navigator when an app is paused at a user-defined breakpoint
Xcode's Debug navigator when an app is paused at a user-defined breakpoint

In the image above, a breakpoint was set by clicking in the gutter on line 65. Sometimes, a single line might have multiple commands, so the green line under the = indicates that the command to assign 0 to forwardIndex is about to happen but hasn’t yet.

At the bottom of the window, the variables area shows the current value of the local forwardIndex variable is 6.

In the navigator area, you’ll see some basic statistics about the app’s CPU and memory usage. You can also see the stack of frames for Thread 1. Thread 1 is the main thread. The item at the top of the list, 0 closure #2, is the current frame. The blue person icon to the left of the 0 means it’s your code. If you were to scroll this list down, you’d eventually get to a frame titled main. That’s your app’s starting point.

There are some controls at the top of the variables area. You use these to step through the code and restart the app.

Resume Step-over Step-out Location Memory Graph Step-in Accessibility View Hierarchy
Debug controls above the variables area

Starting from the left:

  • Clicking the breakpoint icon enables or disables all of the breakpoints in the app. When you have breakpoints set in different parts of your app, it’s helpful to be able to disable all of them as you navigate to where you’re troubleshooting and then enable them.

  • The Resume button toggles between stopping and starting the app’s execution. Click it any time the app is running to send the halt message to the app. As you’ll see, you rarely halt in your own code when you do that. However, halting on one of your breakpoints and then resuming when you’re done looking is more useful.

  • The Step-over button executes the current line and moves to the next line.

  • The Step-in button moves into any function. In the image above, using step-in moves you into the machine code Swift uses to assign one value to another value. When you’re calling one of your own functions, using step-in lets you go into that function to inspect code rather than stepping over.

  • The Step-out button executes all of the code until the end of a scope segment and then pauses.

The next two buttons are used for more advanced view and memory debugging and won’t be covered here. They are the View Hierarchy and Memory Graph debuggers, if you’re curious and want to research them.

The next button is for adjusting the accessibility settings on a simulator. These settings replicate the tools in the Accessibility Inspector helper app and aren’t enabled for most simulators. Use the Accessibility Inspector instead. You can find the Accessibility Inspector under Xcode ▸ Open Developer Tool ▸ Accessibility Inspector.

The last button is for simulating locations in the simulator. This shows when working with location-dependent code. You can choose one of the predefined locations or add your own.

Clicking the step-over button in the example above will execute the line to set the forwardIndex to 0. The currently executing line will update, and the values in the window will change.

The loop in this example is relatively short; maxIndex is 5. What if you had a loop and wanted to examine the 100th or 1000th time through? Instead of manually stepping through, you can modify the values of variables while in the debugging window.

At first, you might try to right-click the variable and select Edit Value… from the context menu. This will initially appear to work but won’t, at least not in the current version of Xcode. The debugger area and some other parts of the debugger in Xcode are just graphical wrappers around a command-line program called lldb, Low-Level Debugger. Unfortunately, as things change, sometimes the graphical portions break. This is one of those times. Perhaps in a future version of Xcode, it’ll start working again, so you can change the value from the context menu.

Instead, activate the debug console by pressing Command-Shift-C or clicking the View Console button in the lower right of the debug area.

Use these buttons to view and hide the variables and console areas of the debugger.
Use these buttons to view and hide the variables and console areas of the debugger.

Note that if your Xcode window is narrow, it won’t let you show the console and the variables at the same time.

The console is an interactive way to talk to the lldb debugger. You enter commands after the (lldb) prompt. For instance, instead of clicking the step-over button, you could type n into the console to step over some code. You can also type c to resume running your app.

The most useful commands you’ll use with the console are:

  • p or po To print the values of variables, expressions, or objects.
  • exp To execute an expression and change the value of something.

The lldb debugger does much more than this, but as you’re starting out, these two commands are the most useful. For example, if you wanted to cause the loop from earlier to execute the code for when forwardIndex > maxIndex is true, you could put a breakpoint on the declaration line of var forwardIndex = currentIndex + 1. When the code halts, you can check the value of currentIndex and then set it so that forwardIndex is greater than maxIndex and the code in the if executes. In the code below, every line beginning with (lldb) is what you’d type, and the other lines show how lldb would respond.

(lldb) po currentIndex
0
(lldb) expr currentIndex = 5
() $R0 = {}
(lldb) po currentIndex
5

Now that currentIndex has been set to 5, the code in the if statement will execute, and stepping through the code, you can watch forwardIndex start as 6 and then change to 0.

For a basic breakpoint, you set it by clicking the line number, and when it’s hit, it stops your code. However, you can make breakpoints do more interesting things.

If you open the Breakpoint navigator, you’ll see all the breakpoints added to the app. From here, you can enable and disable individual breakpoints. You can also set symbolic and runtime breakpoints and edit the behavior of breakpoints.

You can edit breakpoints to execute conditionally.
You can edit breakpoints to execute conditionally.

The image above shows that the breakpoint here has been modified to only execute when currentIndex == 3. Also, instead of stopping the app, it’ll play the frog sound and let it continue running. A breakpoint like this can be handy when you want to know it’s been hit while exercising your app.

See forum comments
Download course materials from Github
Previous: Warnings Demo Next: Breakpoints Demo