Xcode Debugging tips and tricks
As a lazy software engineer, I always look for shortcuts to complete my tasks. And since we spend half of our times debugging let me share with you some debugging tricks I learned in my last programming years or should I say debugging years.
For this article lets assume we have this simple struct:
struct User { var name: String = "Hermione Granger" let birthdate: String = "September 19, 1979" var city: String = "London" var description: String { name + " born in " + birthdate + ". Currently living in " + city + "." }}let user: User = .init()
po command:
Which is the most famous one, is an alias of “expression — object-description ” printing the description method of the given argument within the scope of the breakpoint.
The good part (but not always good) of this one is that it can also execute valid expression and print the result. For example:
(lldb) po user.city.uppercased()"LONDON"
p command:
This one is another way of printing out variables and is also an alias for “expression ” but without the “ — object-description”, the difference is that p command will print the whole object with a special convention name in lldb: $R0, $R1,.. meanwhile po will print the debugDescription of it.
p command does also the compiling and the evaluating part from po, but once it gets the result it, lldb perform a step named dynamic type resolution. This is how it looks to print the object with p command:
(lldb) p self.user(lldbDemoApp.User) $R1 = (name = "Hermione Granger", birthdate = "September 19, 1979", city = "London")(lldb) p user.description.uppercased()(String) $R2 = "HERMIONE GRANGER BORN IN SEPTEMBER 19, 1979 . CURRENTLY LIVING IN LONDON"
Symbolic breakpoints
Which makes the debugger pauses when the app or your code calls the symbol you specify.
To create a symbolic breakpoint click on Breakpoint Navigator and another click on +
symbol at the bottom-left corner and select Symbolic Breakpoint
as an option.
Xcode will pop up all the places where the symbol exists in the Breakpoint Navigator, and will create a breakpoint for it even if you can’t see it established when navigate to that line of code directly.
Now, let’s move to my favourite ones.
v command:
This one is an alias for “frame variable command”, available since Xcode 10.2. v command is significantly fast (especially for those 10 minutes to build projects), because it doesn’t compile or execute code at all. Which means it can only access variables in the current stack, no computed properties no functions or method calls. Let’s try v command
(lldb) v self.user(lldbDemoApp.User) user = (name = "Hermione Granger", birthdate = "September 19, 1979", city = "London")(lldb) v user.name(String) user.name = "Hermione Granger"(lldb) v user.descriptionerror: "description" is not a member of "(test.testApp.User) self.user"(lldb) po user.description"Hermione Granger born in September 19, 1979. Currently living in London."
As you can see above v failed to output the computed property description while po did. This required code to be compiled and executed which the v command does not do.
expression command:
For most of the projects it takes a while to compile, and to debug a possible fix like disabling a toggle or editing a colour you can inject code without the need of re-building or re-running using the expression command. Let’s make use of our struct once last time.
First we need to change the user from let to var,
var user: User = .init()
And then ready to go:
(lldb) expression user.name = "Ron Weasley"() $R0 = {}(lldb) po user▿ User- name : "Ron Weasley"- birthdate : "September 19, 1979"- city : "London"
The expression can also be configured to be executed each time you hit a breakpoint.
For that you can add your breakpoint, and double-click or right-click on it to see a small configuration window, then click on Action and turn it to “debugger command” and then inject the code you want to swap at the time of execution.
To make the debugger continue without pausing on that line you can select the “Automatically continue after evaluating actions” checkbox.
And voila, you code is evaluated and executed in runtime.
Happy debugging, folks!