iOS Debugging Tips & Tricks
Every developer knows that debugging is important.
We spend a lot of time on creating breakpoints, stopping program executions and looking for solutions. There are some nice tips and tricks that help us to do it effectively. Here I will show you some of them.
LLDB is high performance debugger that is build as a set of reusable components. In order to get to lldb you can type “lldb” in terminal. When it comes to Xcode it is default debugger so any stop in the program execution will result in launching it.
help & apropos commands
"help" command dumps out all available commands, including the custom commands you can specify.
If you want to know about e.g. breakpoint command you can type
You can go even deeper in terms of documentation. Just type:
If you don’t know exact phrase to look for, but instead you know a phrase that may be helpful in searching you can use „apropos” command (it is case-insensitive). For example after typing:
You will see everything that might pertain to the word „Swift”
You can also use „apropos” to look for sentence. For example: „ apropos "reference count” ’’. Thanks to quotes „reference count” is considered as one argument.
Let’s imagine that you have a property that you would like to observe (stop every time it got changed) only for some time. To do that you can create additional didSet property observer for it and set breakpoint in Xcode GUI, but you would have to:
- Relaunch app if it’s already launched
- Remove that property observer later on
It may be not such a big problem, but there is a nice way to observe this property and it’s called watchpoint.
To set it you have to select property you would like to observe in left side of Debug Area.
After doing it, this breakpoint will be shown in Breakpoint Navigator.
Every time your property gets changed, LLDB will stop.
One of Xcode breakpoints are Symbolic breakpoints. As name suggests they let you set breakpoint on certain symbol. For example it may be viewDidLoad.
To create this type of breakpoint go to Breakpoint Navigator. In the bottom left, click the plus button to select the Symbolic Breakpoint option.
A popup will appear. In the Symbol part of the popup type: viewDidLoad.
Build and run the app. Xcode will stop every time viewDidLoad function (no matter what UIViewController has it) is called.
The second type is Exception Breakpoint. When something goes wrong in your program and it crashes you can set this this type of breakpoint. It will fire every time an exception is thrown. When this happen Xcode will show you the line that is responsible for this crash.
Finally, there is the Swift Error Breakpoint. It fires when Swift errors are thrown. It is especially helpful in a situation when you want to see when an error is thrown by your code.
Creating breakpoints give us some neat options that we can set:
First of all we can set condition after which debugger will stop on a breakpoint. It could be anything like for example: “titleButton.isHidden == true”
We can declare how many times we would like to ignore stopping on a breakpoint via “Ignore” option.
There is also action that we can trigger after stopping on a breakpoint. You can set many actions on one breakpoint (look at “+-” buttons on the right).
We can for example trigger some special sound. It allows us to quickly and easily identify various breakpoints. Another example is that by choosing “Debugger Command” we can print some variable’s value.
Last but not least, there is a possibility to “Automatically continue after evaluating actions”. It means that program won’t stop program execution but it will continue (with launching breakpoint’s actions). It is useful in some scenarios. Imagine that you measure sound frequency from microphone every 0.5 seconds and you would like to print it’s value to see it without need to manually continue program execution.
There are several different ways to create breakpoints. The easiest way is to just type b and name of your breakpoint. If you want to create breakpoint on some Objective-C library (like for example UIKit) you have to stick to Objective-C code. For example you can type:
You’ll see that output:
There are some informations about created breakpoint. Breakpoint 1 means it is first breakpoint in this debugging session. The more you create breakpoints, the bigger is breakpoint ID.
If you would like to set breakpoint on property observer (didSet/willSet) on for example title property of article model you can do this by typing:
The rb command will get expanded out to rbreak (provided you don't have any other LLDB commands that begin with „rb”). It is regular expression breakpoint -> shortcut for breakpoint set -r %1.
It can be found by image lookup command (described above).
When you debug your program there are two debugging context: Swift and non Swift debugging context. Using pause icon to stop program will end up in stopping in Objective-C context while stopping via Xcode breakpoint (in Swift code) - in Swift context.
Trying to execute Objective-C code in Swift context will end up in error (and vice versa). Fortunately, there is a possibility to select language through -l option. Just type:
User defined variables
You can define your own variables. To do it you have to type:
Remember about „$” character since LLDB needs it to create local variable. For now creating objects in Objective C context and trying to achieve them in Swift context (and vice versa) works only if you choose language (-l option, explained above).
Creating references in LLDB can be used in real life situations! For example by stopping on breakpoint and defining your own variable LLDB will create it. Then you can just type „po $R0.buttonTitle = ”example” ” to trigger setter for this title. Just remember here to stay in one context.
po & p
I think that everyone knows po command. It prints object description, while p prints more raw representation of the object. There is one neat thing about p. After typing for example:
It will not only print object information, but also creates for us local variable „$RX” (where X is number) which we can use later in this particular debugging session.
From Xcode 10.2 there is new frame variable command "v" that is faster and recommended by Apple to use instead of p and po. It will not evaluate the expression, but will print variables in the current stack frame.
When working with LLDB there are some important navigation actions that will help you in your development. These are step over, step in and step out.
Stepping over allows you to go to the next code line in the context where you stop. It means that if current position is calling another function this function will run to the end and then LLDB will stop.
Stepping in means executing the current statement and stopping at the next statement. The difference between stepping in and over is that in stepping in if current statement is function LLDB will step into execution of that function and then stop
Stepping out means that function will stop after it has returned.
Xcode GUI - Stepping
Here you have these 3 buttons in Xcode:
In GUI there is nice trick about step over and step in buttons. Hold Control + Shift and click on one of them -> you will be able to manually control the execution of different threads.