Debugging: How To Find And Fix Bugs With The Xcode Debugger

Written by: Reinder de Vries, May 31 2017, in App Development

Debugging: How To Find And Fix Bugs With The Xcode Debugger

Finding and fixing bugs sucks! In this article, you’ll learn how to let Xcode do the dirty work for you, by debugging with print(), breakpoints and exception handling.

Your ability to spot and remove bugs from your code greatly influences how good you are at coding. Learning about the debugging tools in Xcode is important if you want to become a better app developer.

Ready? Let’s go.

  1. 3 Different “Categories” Of Bugs
  2. The Bug Fixing Process
  3. Poor Man’s Debugging: Using print()
  4. Stepping Through Your Code With Breakpoints
  5. Using Exception Breakpoints
  6. Further Reading

3 Different “Categories” Of Bugs

Let’s start by setting the stage for fixing bugs. You’ve just coded a feature in your app, and just as you’re about to finish implementing the feature – your app crashes.

Oops! There’s a bug somewhere! What now?

Fixing bugs, as frustrating as it may sometimes be, can be very exciting. It’s like being a detective, but then with code. You get to trace the bug back to its roots, and then solve it. It may sound strange, but solving bugs can be very fulfilling.

Think about how much a bug tells you about its surrounding system, and code, and how much you can learn. You can become a 10x better coder just by solving bugs…

In general, there are 3 types of bugs:

  1. Bugs that have a clear error message, like “Index out of bounds”
  2. Bugs that don’t have a clear error message, like EXC_BAD_ACCESS
  3. Bugs that don’t have an error message at all

There’s a fourth category. Sometimes your Swift code doesn’t work as intended, but doesn’t crash either. In many cases, there’s a functional bug, but not a bug that results in a complete crash of your app.

When you’re finding and fixing bugs, you’ll always want to work towards an error message. An error message for an app developer is like evidence for a detective.

Get 5 of my best practices

Get 5 of my best practices

Learn how to build better iOS apps

I’ll show you exactly how I built a dozen professional iOS apps,
write extensible Swift code, and turn coffee into code.
Wait, what? Yup – into Swift code.

The Bug Fixing Process

You can define the “bug fixing process” like this:

  1. Your app crashes. There’s a bug! (It also happens that you simply notice yourself that your app doesn’t function as intended.)
  2. You attempt to find the error message. Error messages are often shown in the Xcode debugger, but sometimes you have to dig a little deeper.
  3. You then interpret the error message – what does it mean?
  4. You come up with a solution to solve the bug, and then try it out. If this doesn’t solve it, you go back to step 2 or 3.

This makes bug fixing sound really simple, right? Obviously, a lot of work can go between step 3 and 4 – the bug fixing process is simple, but not easy. (There’s a difference!)

In many cases, especially if you’re a beginner coder, you immediately Google the error message you found. This usually brings you to StackOverflow, a blog, Quora or a similar search results. In most cases StackOverflow, or a related blog, has the answer to the error you’re seeing.

Based on what you found with Google, you implement the solution in your own app. Even though most StackOverflow answers can explain well what the cause of the bug is, and it’s solution, implementing the solution in your own app still requires you to do customized coding work.

You can of course also ask for help fixing bugs, by posting a question yourself, or by asking another programmer.

It often helps to explain the problem to someone else – I call this “Solve by House”, named after the problem-solving technique the fictional Dr. House uses in the TV series House M.D. Programmers often call this rubber duck debugging. By explaining the problem to someone else, you carefully step through the problem, and this usually gives you an insight in a solution. Try it!

However, when a ready-made solution can’t be found on the internet – how do you debug your Swift code?

Build better iOS apps by mastering best practices and app architecture » Find out how

Poor Man’s Debugging: Using print()

The function print() is a great tool for debugging. Say you’ve got a bit of code, like this:

var names = ["Bob", "Alice", "Arthur", "Ford", "Zaphod"]

let name = names[8]
// Error: Index out of range

The above code throws an error, of course! The names array has 5 items, so when you try to access item no. 8, the code crashes and shows you an Index out of range error – the item you’re trying to access doesn’t exist!

You could use breakpoints to solve the problem (see below), but a print() statement does wonders too:

print(names.count)

The print function will write some text to the output window in Xcode, so you can see the output your app generates. See the image below:

How To Find And Fix Bugs With Xcode Debugging

With the above code you can figure out the number of items in names, and then realize that this number is smaller than 8, and therefore the bug occurs.

The hardest part is of course: where do you put the print() statement? Your code crashes when the error occurs, so you’ll have to figure out where the bug occurs.

This is the most important part in debugging your Swift code: Where does the bug occur? In many cases, Xcode can tell you the exact line. So, you then put the print statement above that line and use it to inspect your code.

As you’ll see, using breakpoints and using the print function are similar debugging methods. You inspect your code, and by inspecting your code you gain insight into the state of your app at a certain point, and that’s information you can use to solve the bug.

Quick Tip: You can use literal expressions to print function names, line numbers and filenames with #function, #line and #file. That way you know exactly where print output comes from. Like this: print("\(#file):\(#line), \(#function) --- FUSRODAH!") or print(#function). Neat!

Smash bugs with the Xcode debugger – learn how: https://learnappmaking.com/debugging-fix-bugs-xcode-debugger-breakpointsClick To Tweet

Stepping Through Your Code With Breakpoints

Another, better method of debugging your Swift code with Xcode exists. Xcode has an actual debugger.

The debugger can stop your code at almost any moment and show you the exact state of your app at that point. You can do that by adding a breakpoint to a line of code in your app.

When the app executes, and then “hits” that line of code, execution of your code comes to a halt, and you can look inside the gut of your app.

As you can see in the image below, using the Debugger you can inspect several variables and their values:

Xcode Debugger Breakpoints

In the image you see:

  • The stacktrace. You can think of the stacktrace as a “function history” or “breadcrumb”. With it you can see the functions that were called before the breakpoint was reached. For instance, in the screenshot you can see the current function, viewDidLoad().
  • The breakpoint. You can set a breakpoint by clicking on the gutter, right before a line. A blue arrow should appear. You can deactivate the breakpoint by clicking it again, and remove it by right-clicking.
  • The breakpoint actions. You can take several actions when a breakpoint is hit, like continuing execution. More on these actions, later.
  • The app values / state. You can inspect the values at the current point in the app’s execution. In the example, you can clearly see the contents of the names array.

When you look at the bug in the previous example, where you tried to access item with index no. 8 in an array with only 5 items, you see that using a breakpoint you can inspect the size of names. This has the same effect as print – although breakpoints are more advanced.

Quick Tip: Xcode Playground doesn’t have a debugger, but you can directly inspect your code’s values while coding!

Take a look at the debugger UI:

Breakpoint Actions Debugger

From left to right, you’ve got several options:

  1. Hide the debug area. Pretty self-explanatory!
  2. Deactivate breakpoints. This button deactivates all breakpoints for the current session.
  3. Continue program execution. This will continue execution of your code, until the debugger hits the next breakpoint. Keep in mind that this can be the same breakpoint, for instance when your code repeats or reaches the same point again.
  4. Step over. This continues the code, but only to the next line.
  5. Step into. This continues the code, by stepping into the current line. You go one level deeper, for instance by going into another function.
  6. Step out. This continues the code, by stepping out of the current function. You go one level higher.

Those last 3 actions are interesting. Think of your code as a tree-like structure. You start at one point, then go into a function, and when the function finishes, you go out of the function again. It’s like how a caterpillar would eat through a cake, with different levels.

When you’re going over your code line-by-line with breakpoints, you can decide to “step over” a function, or go “into” that function. When you go in, you can go out again too.

Quick Tip: When a breakpoint hits, the line it hits on isn’t executed yet. You can clearly see that in the above screenshots. The app crashes on the line that has the breakpoint, but you can see in the debug output that the app hasn’t crashed yet (no error message). So, when you step over line 21, i.e. execute that line, the app crashes.

Practicing stepping through lines of code is easiest when you code a simple loop. You can see in the image below that there’s a for-loop that calculates the sum of an array of numbers. The code breaks on the line inside the for-loop.

Make sure that you understand this, about the example above:

  • The loop has ran for 5 times, from n = 0 to 4
  • At this point, n = 5 but numbers[5] hasn’t been added to sum yet
  • The variable sum is equal to 1 + 3 + 7 + 9 + 14, the sum of numbers[n] from 0 to 4

Stepping into a function isn’t always possible, or helpful, because you can only go over your own code step-by-step. You can’t inspect the source code of UIKit frameworks, so when you try to go in, you’ll only see assembly code. (You can debug this, but that’s for another blog post…)

So… how does this help you solve bugs? Well, you can inspect the values your code has. In many cases, bugs occur because your app gets to a state that you hadn’t expected. Inspecting your code often gives you insight into the state of your app, and helps you to solve bugs.

Get 5 of my best practices

Get 5 of my best practices

Learn how to build better iOS apps

I’ll show you exactly how I built a dozen professional iOS apps,
write extensible Swift code, and turn coffee into code.
Wait, what? Yup – into Swift code.

Using Exception Breakpoints

In some cases you can’t set a breakpoint in your code, because a bug occurs in a framework or Cocoa Touch library. When you can’t reach those lines of code, you can’t set a breakpoint.

How do you debug that? That’s where exceptions come in. You might know them from Swift error handling with do-try-catch. Some functions can “throw” exceptions when an error occurs.

You’re supposed to catch those errors with a do-catch block, but in many cases you deliberately want to crash the app so you can solve the bug that causes the exception.

A fairly typical exception is the NSInternalInconsistencyException. This is a general error exception that’s thrown in a scenario where your code gets to a state it can’t support.

When such an exception is raised, Xcode won’t always take you to the line that caused the exception. You can still find it, like this:

Exception Breakpoint

Instead of setting a breakpoint on a specific line, you set a breakpoint for any exception that’s thrown in your app code. When an exception is thrown, Xcode will take you to the line of code that caused it. This often helps you to pinpoint the line of code that causes the error.

You can set an exception breakpoint by going to the Breakpoint navigator, on the left of Xcode, and then clicking on the small +-button at the bottom-left. Then, choose Exception Breakpoint. You can also add several other breakpoints, for instance for the Swift Error type. That’s it!

Further Reading

Solving bugs is exciting! You get a chance to dive into your app’s code, figure out why it isn’t working, while learning about development. Debugging can also be frustrating…

Want to learn more? Check out these resources:

Enjoyed this article? Please share it!

Debugging: How To Find And Fix Bugs With The Xcode Debugger https://learnappmaking.com/debugging-fix-bugs-xcode-debugger-breakpointsClick To Tweet

Reinder de Vries

Reinder de Vries is a professional iOS developer. He teaches app developers how to build their own apps at LearnAppMaking.com. Since 2009 he has developed a few dozen apps for iOS, worked for global brands and lead development at several startups. When he’s not coding, he enjoys strong espresso and traveling.

Comments & Questions

Got a comment or question? Let me know! I read and respond to every one. Thanks!