Breakpoints and Examining variables in Xcode

craiggrummitt
14 min readNov 30, 2017

--

Save 37% off iOS Development with Swift with code fccgrummitt at manning.com.

Imagine you have just built an awesome iOS app for storing details of the books you own.

The app has two main scenes — a scene containing a table listing all of your books, and another scene where you can add or edit the details of a book. You can even add a cover image either with the device’s camera, or by detecting the book’s barcode.

Now — imagine that you sent your app to a friend to get their feedback, and they responded with the following:

“After you add an image and save it, the next time you edit the book and save it, the book cover seems to disappear… strange?”

Well. That is strange — looks like you have some debugging to do!

In this article we’re going to explore methods for diagnosing and resolving this problem. We’ll be looking especially at breakpoints, and different techniques for examining variables in Xcode.

Downloading the code

Before we do anything, we need to download the code for the app. To do that you’re going to clone the project from within Xcode.

1. Select Source Code -> Clone.

2. Enter the URL of the repository in the text field: https://github.com/iOSAppDevelopmentwithSwiftinAction/Bookcase.git. The Xcode project should download.

The default branch will be selected, but we want to select a different branch.

We can select a branch in the Source Control navigator which you’ll find in the column on the left of Xcode, called the navigator area.

3. Select the second tab from the left. This should open the Source Control navigator.

4. Open up the Bookcase repository and you should see three groups — Branches, Tags and Remotes.

5. Select Remotes and within Origin, find the Chapter15.1b.UpdatesNeedFixing branch. Right click on this branch and select Checkout to checkout the correct branch. As you haven’t downloaded this branch before it will download.

6. Connect up an iPhone or iPad and check you can run your app on the device by clicking the play button at the top left.

7. Select the skip button to bypass onboarding.

Replicating the problem

The first check that you need to do is to try and replicate the problem. See figure 1 for a visual representation of the problem that your friend described.

Figure 1 How to replicate the bug

Let’s try and replicate the bug.

  1. Select a book from the list.
  2. Select the camera button, add a cover image and select save.

You should now see the cover image in the main table scene. All good so far!

3. Open the same book again and select save. The book image returns to the default cover image. Strange is right! What could be happening?

Your immediate suspicion is that for some reason, an existing book cover isn’t being used when the BookViewController generates a book to save. Let’s confirm that, by examining the bookToSave variable in the BookViewController class, in the touchSave method.

As is so often the case in Xcode, there are many different ways to examine the contents of a variable. Let’s look at a few now, beginning with a method that you’ve probably seen before, the print method.

Examining a variable with print

To examine the bookToSave variable, let’s print its contents to the console with the print method.

1. Before the touchSave method calls dismissMe, print the bookToSave variable:

print(“Saving book: \(bookToSave)”)

2. Run the app again, once again add a cover image to book, select save, reopen the book and select save.

This time, the book object should print to the console, something like:

Saving book: Book(title: “Five on Brexit Island”, author: “Enid Blyton”, rating: 3.0, isbn: “ 9781786488077”, notes: “”, image: Optional(<UIImage: 0x1c02aeb20>, {128, 202}), backgroundColor: UIExtendedGrayColorSpace 1 1, primaryColor: UIExtendedGrayColorSpace 0 1, detailColor: UIExtendedGrayColorSpace 0 1)

Well, that’s great. By default you’re seeing the value of every property of the object, down to its background color. Sometimes, however, when you print an object, you might not need to see its every last detail. You might prefer to see just the important stuff. It would probably be sufficient detail to identify a book, for example, by the title and author. To resolve this bug, you might also want to see whether or not this book has a cover image.

There’s a neat little trick for adjusting the string that’s output when you print an object. If your custom type adopts the CustomStringConvertible protocol, you can provide a description property that describes your object as a String, and it will automatically be used by print.

3. Set the Book struct (you’ll find it in the Model group) to adopt the CustomStringConvertible protocol now:

struct Book: Codable, CustomStringConvertible {

4. Add a description property to the Book struct that returns the title, author, and a message about whether the Book has a cover image:

5. Run the app again and save a book with a cover image. This time you should see some more meaningful information about the book being saved in the console:

Saving book: Great Expectations by Charles Dickens : No cover image

But wait — “No cover image”? It appears as though your suspicion was correct. For some reason the book object to be saved isn’t being generated with its cover image.

Tip Classes that subclass NSObject, such as UIView, automatically adopt the CustomStringConvertible protocol and contain a description property. To provide your own description, you’ll have to override the default description property.

Sometimes, adding print statements everywhere in your code to help diagnose a problem can just get out of hand, and more sophisticated debugging techniques would be more appropriate.

Tip An alternative approach to print that some developers prefer is the NSLog statement. While NSLog is a little slower, it does add a timestamp to the log and stores logging data to disk. Having a log history can be useful, but makes it all the more important to ensure you remove all NSLog calls from your code before publishing your app to the App Store.

Remove the print statement now. We’re going to explore some other debugging techniques to diagnose the source of this problem further.

Pausing your app with a breakpoint

To diagnose problems in your app, sometimes it can help to use a file and line breakpoint to pause execution at a line in your code. File and line breakpoints are ultra-useful for:

  • Checking the current state of the app. This is useful to take a closer look at variables, the call stack, threads, the user interface or the app’s use of system resources at a very specific point in time.
  • Stepping through your app. You can use the step controls to run your app step by step and diagnose any problems with the flow of your app.

You’re going to use file and line breakpoints to analyze why books aren’t being saved with their images. Let’s start by taking a look at just after a book object is generated for saving data from the book edit form.

  1. Add a breakpoint to your code just after setting the bookToSave variable in the touchSave method in BookViewController. Adding a breakpoint is simple — just click to the left of the line where you would execution to be paused. A dark blue pointed rectangle should appear where you have clicked, indicating an active breakpoint. See figure 2.
Figure 2 File and line breakpoint

Note Be careful not to click on the breakpoint again — this will cause the indicator to turn light blue, and the breakpoint will toggle to a disabled state.

Another interesting place that could be interesting to analyze is after the BookViewController class receives a Book object to edit.

2. Using the same technique, add a second breakpoint to the viewDidLoad method of BookViewController, just after unwrapping the book object.

3. Run your app again, and this time tap on a book that does not have a cover image. The app should pause immediately at the breakpoint you specified in the viewDidLoad method.

The debug navigator and debug area open automatically for you and the paused line of execution highlights in green. See figure 3.

Figure 3 Breakpoint pausing execution

Advanced Breakpoints

Most commonly you’ll use breakpoints to pause execution at a specific line of code, but they are capable of doing so much more.

For example, there are breakpoints that break execution whenever specific types of exceptions occur (called an exception breakpoint). There are also breakpoints that break execution whenever a specific method is called on all subclasses of a certain type of class (called a symbolic breakpoint). You would need to add these types of breakpoints in the breakpoint navigator.

Your breakpoint could be set up to trigger only if a certain condition is true, or after a certain number of times. Breakpoints can be set up to also be used to perform one or more actions — such as output to the console, or play a sound. Ironically, breakpoints don’t necessarily break execution either — if you like, after performing an action, a breakpoint can automatically continue.

Edit your breakpoints by double clicking on the breakpoint indicator in the source editor or the breakpoint navigator.

Now your app has paused execution, we can examine the state of the app’s variables. Checking the book object at this point may help us to diagnose the problem with saving a book cover.

There are several approaches for examining the state of variables while the app is paused:

  • The variables view
  • Quick look
  • Print description
  • Command line in the lower level debugger
  • Datatips

We’ll look at each of these in turn. Let’s take a look first at the variables view.

Examining a variable with the variables view

The variables view contains variables in the context of where the app is currently paused. Instance variables of BookViewController will be contained within the self property, while local variables are shown at the top level. As the book object is unwrapped with optional binding, it is considered a local variable.

At the left of some variables, you’ll see a disclosure triangle, indicating that you can ‘open up’ the variable to have a closer look at their contents.

  1. Click on the disclosure triangle for the book object to inspect the value of its properties. See figure 4.
Figure 4 Variables view

Note that the book image is nil. This makes sense as you selected a book with no cover. Now let’s resume execution so that we can add an image to this book.

Controlling the app’s execution using the debug bar

Above the variables view you’ll find the debug bar, which contains several controls useful for controlling the execution of your app. See figure 5.

Figure 5 Debug bar

Some elements could use extra explanation:

  • Toggle breakpoints: For convenience, toggle all breakpoints on or off.
  • Continue / Pause: Continue execution of the app.
  • Step buttons: There are three skips buttons for executing your code step by step. Step over and step into differ as to how they act when there is a method call in the current line. Step into will step through every line of the method, whereas step over will interpret the entire method as one step. Step out on the other hand, executes the rest of the current function as one step, and pauses execution again when it exits the function.
  • Debug view Hierarchy: View the hierarchy of views in the app.
  • Memory graph: Visualize the memory allocations in the app.
  • Simulate location: Simulate that your app is running from an alternative location.
  • Jump bar: Use the jump bar to examine your app state from the context of different threads and stack frames.

Let’s use the controls in the debug bar to resume execution of the app.

  1. Tap the Continue button.
  2. Add a cover image to the app.
  3. Save the book with the new image by tapping the Save button.

The app should pause execution again, just after generating a new book to save in the local bookToSave variable. Let’s examine this variable for more clues.

Examining a variable with quick look

  1. Focus once again on the variables view, and select the disclosure triangle beside the bookToSave variable to open it up.
  2. Note that this time the book image shows a memory address. You can reasonably assume that this means that our book contains an image, but how can you know which?

Some variables are visual in nature, and the variables view may not be sufficient to describe a variable. Quick look provides you with a visualization of the contents of a variable.

3. Select the image property of bookToSave.

4. To open a visualization of the image property, select the button that looks like an eye, below the variables view. See figure 6.

Figure 6 Quick look

Well, that seems to have worked correctly. The image we added to the book edit form is being stored in the image we are saving. But the problem was presenting itself in books that already have an image. We’ll need to go through this process again, with the same book now we know it contains an image and find the source of this problem.

5. Tap the Continue button, which should return you to the main screen.

6. Choose the same book you just added a cover image to.

The app should pause once again at the breakpoint in the viewDidLoad method just after unwrapping the book object to edit.

Let’s use yet another method to examine the contents of the book object.

Examining a variable with print description

Next to the show quick look button, is another useful button that appears an ‘i’ in a circle. This is called the print description button. If you select a variable in the variables view, and select the print description button, you will get the same output in the console as you did earlier when you printed a variable in code.

This time, you’re going to examine the contents of the book object with the print description button.

  1. Select the book object in the variables view.
  2. Select the print description button.

The description property of the Book object that you set up earlier, will output to console. See figure 7. Covering all bases, the properties of the Book object also output to the console.

Figure 7 Print variable description

Well, according to the output it seems as though there’s still no problem with the book object. We will have to continue execution and save the book to see if the problem is happening there.

But first — what’s that strange (lldb) message that crops up in the console?

Examining a variable with lldb

The console is much more than just an area for receiving debug logs and outputting print messages. It is actually a window into the powerful command-line debugger called lower level debugger, and the (lldb) message is actually a prompt for you to enter commands.

Many debugging features we use in this chapter are GUI representations of lower level commands that are available to you as command-line commands in the console.

For example, the print description button you just pressed to explore details on the book object, uses the po command under the hood.

Figure 8 LLDB command po in the console

1. Use the po command to examine the book variable. Type the following after the (lldb) prompt and press return:

po book

You should see the same description appear for Book that you set up earlier. See figure 8.

Figure 9 LLDB command p in the console

If you want to go beyond the default description of a variable and print the underlying implementation of an object, use the p command.

2. Use the p command on the book variable:

p book

See figure 9 to see the result from the p command. This time you should see a much more detailed output of the contents of the book variable.

We have barely scratched the surface of what’s possible in LLDB. Apart from online documentation, you can use LLDB’s help command to get a comprehensive listing of debugger commands.

For a change, let’s use LLDB to resume program execution.

1. Type c after the (lldb) prompt, and press return. The program should continue.

2. Tap the Save button, to test saving this book.

Once again, the app should pause execution right after generating a book to save. Let’s use one final technique to examine the contents of the book to save.

Examining a variable with data tips

Believe it or not, there’s yet another way to examine the contents of your variable, and this time, you don’t even need the variables view or the console!

With app execution paused, you can just go ahead and point your cursor in the source editor at a variable you would like to examine, and a data tip for that variable will pop up. From there you can open the variable the way you did in the variables view, select to show quick look or select the print description button.

  1. Point to the bookToSave variable now. A data tip for the variable should appear.
  2. Select the disclosure triangle to open up the variable to examine its contents. See figure 10.
Figure 10 Examine a variable with data tips

Notice that this time, the image property of bookToSave is equal to nil. We seem to be getting closer to the problem!

Solving the save problem

Why would the image property be nil? Take a look at how the bookToSave object is generated — the cover image comes from the coverToSave property. Ok, where is this property set?

A quick search for the coverToSave property uncovers the problem. The coverToSave property is only set in two places — when the user selects a photo or image for the book, or when the booksService returns an image after the user scans a barcode. What about books that already have an image? The coverToSave property is never set.

  1. In the viewDidLoad method of BookViewController, set the coverToSave property after unwrapping the book object. Check first that the book has a cover image, to avoid setting the default cover to the coverToSave property.

Run the app again, select a book with a cover image, and save it. This time (fingers crossed!) the book cover image should stick around. Hooray!

Good job, detective — problem solved. You can remove our two breakpoints now.

2. To remove the breakpoints, simply click on them and drag them to the right. They should disappear — in a puff of smoke!

Examining a variable in summary

There are many methods for examining the contents of a variable, each with their own advantages:

  • print: If you prefer to not pause execution of your app.
  • NSLog: If you would like timestamps on your console logs and a log history.
  • Quick look: If you would like a visualization of the variable’s contents.
  • Datatips: If you’re short on screen space and would prefer to hide the debug area, or if you prefer to explore variables in the context of your source code.
  • p command in LLDB: If you need information beyond what the default description returns for the variable.
  • Variables view: If you would like a visual representation of the hierarchy of variables in your app.

We’ve only scraped the surface of debugging in Xcode. For more on debugging, you should definitely take a look at chapter 15 of iOS Development with Swift. There’s much more covered, from diagnosing crash logs to gauges and instruments.

You can check out the first chapter of iOS Development with Swift for free and see this slide deck.

--

--