Inspecting and Changing Visual Elements With LLDB and View Debugger

Gabriel Sória
The Startup
Published in
8 min readSep 15, 2020

Have you ever added an element in an UIView and after checking in the simulator you realized that it was not what you were expecting?

Well, our beloved Xcode has a very useful feature for inspecting visual elements, which is called View Debugger. This feature can be activated while your app is running by clicking in the button indicated in the image below, located in the debugger bar.

Getting to know the View Debugger

After clicking on the View Debugger button, Xcode will pause the execution of your application and capture the state of your current window.

A screen similar to the one below will appear for you. Let’s try to understand what Xcode is showing us.

Image from Apple developer portal

Exploded 3D rendering of app views: The title describes the function itself. This area will show an exploded 3D view of your current UIWindow, showing all the elements.

View hierarchy: Shows a list of all views and its constraints in the current UIWindow hierarchy. Views that are created by the system will also appear in this hierarchy.

View controls: These controls will change the visualization of the current exploded view, it is possible to inspect the views in 360 degrees and also zoom in/out, show/hide views and constraints.

Inspector area: In this area, you can see pieces of information about a selected object in the exploded view or the view hierarchy. The Object tab shows basic information about the selected view, the Size tab shows information about the size of the view and its constraints. It is also possible to select constraints in the view hierarchy and the exploded view and check its information details.

You can also get information from a view by clicking the right button in the view hierarchy or the exploded view. You will be able to print information in the console, emphasize constraints, hide views that are in the front, or behind the view you selected.

However, my favorite part of the view debugger is using it along with LLDB and the simulator. It is possible to inspect and change the properties of the elements in the current hierarchy without recompiling the project.

Changing properties of a UIView

Build and run the project in this link and click in the button to activate the view debugger as explained before.

You will see a hierarchy similar to the one below. The only one that is important for our context now is the UILabel.

In case the hierarchy does not appear for you, use the shortcut command + 7 in Xcode, click in the button referenced in the image below, and select the View UI Hierarchy option.

We are going to change the background color of that label and see the change without recompiling the project. This is very simple but may open some doors in your current workflow and projects.

In the view hierarchy, click with the right button in that label and then select Print Description of UILabel — View Debugger is Awesome.

You will see in the console some information about that label:

See that 0x7f9c0cd07940? This is the memory address where the label is allocated. By the way, the address that will appear in your console is probably different than mine.

Moving on…

Copy that address that appeared in your console, we are going to use some Objective-C in the debugger. (Don’t worry, Objective-C will not bite you).

e (void)[0x7f9c0cd07940 setBackgroundColor: [UIColor redColor]]

Well, if you check in the view debugger and the simulator nothing will be different (yet), it is necessary to execute another command for the magic to happen.
Type the following command and check the result in the simulator.

e (void)[CATransaction flush]

🙃🙃🙃

To see the result in the view debugger, you will need to click in the continue button in the debugging tools bar in Xcode (or type continue in LLDB), then click in the view debugging button again.

Worth it to remember that by clicking in the view debugger button, we get into the Objective-C context of LLDB, that is why we can’t use Swift.

Moving on…

On view debugger, take a look at the constraints of that label and make Xcode show in the console the memory address of the height constraint, you can do that in the same way you did to show the properties of the label.

In case it was not clear for you how to do that, you should click with the right button in the constraint in the view hierarchy as the image below shows.

… or by selecting the label in the exploded view, then in the debugging tools bar selecting the Show Constraints option.

You will see that the constraints of the label are highlighted. Select the height constraint and select the option to print the description.

Copy the memory address that appears for you in the console and type the following on LLDB:

e (void)[((NSLayoutConstraint *)0x6000001b0a80) setConstant: 200]

And then, update the view with the following command and take a look at the simulator.

e (void)[CATransaction flush]

Very interesting, right?

Of course, you could do that by placing a breakpoint in the ViewController and change these properties, but it is important to know that there are other options to do that and these other options may fit better in your context.
The biggest benefit of using the breakpoints approach is that you don’t need to use the memory addresses of the views and it is easier to even add new elements in the current context.

Adding a UIView with LLDB

Place a breakpoint in the viewDidAppear method of the ViewController class, then build and run.

When LLDB appears for you type the following:

e UILabel()

Some information will appear in your console, but the important for us right now is the variable that LLDB created for us. In my case, it is named `$R0`.

Let’s have some fun with this variable.

Type the following in LLDB:

e $R0.backgroundColor = .greene $R0.translatesAutoresizingMaskIntoConstraints = falsee self.view.addSubview($R0)e $R0.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = truee $R0.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = truee $R0.heightAnchor.constraint(equalToConstant: 200).isActive = truee $R0.widthAnchor.constraint(equalToConstant: 200).isActive = truee CATransaction.flush()

Now, check the results in the simulator.

Well, of course, that is not how you are going to build an application, but it is very important to know that sometimes you don’t need to recompile the whole application, you could just use LLDB in your favor.

Bonus

Did you realize that we used the CATransaction.flush() command a few times? What do you think about creating an alias for that, then we wouldn’t need to type it all the time.

We could go to LLDB and type:

Objective-C


command alias 🚽 expression (void)[CATransaction flush]

Swift


command alias 🚽 expression CATransaction.flush()

By doing that, every time you execute the 🚽 command it would execute the flush method of CATransaction class.

However, if you exit Xcode, it would be necessary to create this alias again, but we have a solution for that.

Every time LLDB is invoked, it looks for some initialization files, one of them is the .lldbinit file.

Go to the Terminal and type the following:

vim ~/.lldbinit

If you ever used this file before, the commands you have already added to it will be there, if you haven’t, the file will be empty.

Add the following to the file:

command alias 🚽 expression CATransaction.flush()

Save the file and exit vim.

In case you already have Xcode opened, you will need to execute the command command source ~/.lldbinit next time LLDB appears. This command will reload the ~/.lldbinit file.

Now, next, you need to execute the flush method of CATransaction class, you just need to type 🚽 and enter. Very cool, right?

I’m pretty sure it gave you an idea of how powerful LLDB is and how much this init file may help us. We can even pass arguments for some commands in this file, for example, add the following command into your ~/.lldbinit file.

command regex — tv ‘s/(.+)/expression -l objc -O — @import QuartzCore; [%1 setHidden:!(BOOL)[%1 isHidden]]; (void)[CATransaction flush];/’

In case Xcode is opened, save and reload the ~/.lldbinit file.

Build and run our example project, then pause the execution of the app using the pause button in the debugging tools bar.

When LLDB appears for you, type the following command:

tv [[[UIApp keyWindow] rootViewController] view]

😇😇😇😇

Looks like everything is gone hehe.

Execute the command again and everything will be back to normal.

Quick tip: You don’t need to type the whole command again, you could just press enter and LLDB will execute the last command you executed. You could even navigate through the past commands you executed by using the up and down keys of your keyboard.

I’m not getting into the details of what this last command exactly does (what about in a next article? Let me know if you would like.), but it is very clear of what LLDB can do for us in terms of changing visual behavior without having to recompile the whole application.

Hope you liked it! Follow me on Twitter.

See you soon.

References:

Apple view debugging

Ray Wenderlich

--

--