Debugging Autolayout issues in the Xcode

With the release of new iPhone devices in addition to older models iPhone4 and iPhone5, developers are forced to switch to autolayout instead of using manual frames to update the view layouts.

As we extensively use autolayout, goof-ups are very much likely to happen. I always miss something or add unnecessary constraints which sometimes crashes an app or spits out undesirable warnings in the console.

Today, I will tell you my story of how I debugged breaking constraints from this blog post. I will tell you techniques you will need to use to debug warnings associated with breaking autolayout constraints, as well as how to decode cryptic console error messages to eventually fix the breaking constraints.

First, to demonstrate my point, we will add conflicting constraints to the view which will result in one of them breaking and spitting console warnings.


let sampleView = UIView()
sampleView.translatesAutoresizingMaskIntoConstraints = false
sampleView.backgroundColor = UIColor.yellow
sampleView.accessibilityIdentifier = "Sample View"
self.view.addSubview(sampleView)
        
self.view.accessibilityIdentifier = "Super View"
let views = ["sampleView": sampleView]
self.view.addConstraints(
    NSLayoutConstraint.constraints(withVisualFormat: "H:|[sampleView(100)]|", 
    options: NSLayoutConstraint.FormatOptions(rawValue: 0), 
    metrics: nil, 
    views: views)
)

self.view.addConstraints(
    NSLayoutConstraint.constraints(withVisualFormat: "V:|[sampleView(100)]", 
    options: NSLayoutConstraint.FormatOptions(rawValue: 0), 
    metrics: nil, 
    views: views)
)
        

As you can see above, I have intentionally added an ambiguous constraint

"H:|[sampleView(100)]|"

Which says,

  1. Set the width of this view to 100px
  2. Also, stretch this view to the device's full width

In order to better debug, I have added accessibilityIdentifier to view and corresponding superview. This will help us to easily spot views with breaking constraints. When I run this app, I get the following debug log letting me know about broken constraints.

As it is clear from debug log, Xcode will also tell which constraints are breaking as a result of the ambiguous layout and also the accessibilityIdentifier associated with it.


Will attempt to recover by breaking constraint

<NSLayoutConstraint:0x600002afc910 H:[Sample View]-(0)-|   
(active, names: Super View:0x7fba4e5060b0, 
Sample View:0x7fba54204bf0, '|':Super View:0x7fba4e5060b0 )>
Note that since we have added accessbilityIdentifier to views, they are immediately recognizable in the debugger. This is a nice trick to apply for easier debug logs readability

As suggested by the debugger, we will add a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints. This will halt the execution when ambiguous constraints are encountered.

  1. Go to Breakpoint navigator
  2. Click on + icon at the bottom-left corner
  3. Choose an option Symbolic Breakpoint
  4. Enter UIViewAlertForUnsatisfiableConstraints under symbol option
  5. Press enter
  6. Restart the app

As expected, it will halt the program execution and present with the following assembly code which you can further analyze and use to debug breaking constraints.

Now going back, let's take a look at debug log, we get the following addresses for respective views
  • Sample View: 0x7fba00e07c20 - View with breaking constraints
  • Super View: 0x7fba00e081d0 - A parent view associated with a view having breaking constraints

There are two ways we can dig this further,

1.  Through View Debugger

This is the easiest way to capture the view with breaking constraints. When Xcode detects the view with breaking constraints, it will print a message which looks something like this,

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x600000f7d590 H:[Sample View]-(0)-|   
(active, names: Super View:0x7fba00e081d0, 
Sample View:0x7fba00e07c20, '|':Super View:0x7fba00e081d0 )>

Looking at this log, you will know that the view Sample View is undergoing breaking constraints and the address associated with it is 0x7fba00e07c20.

The next step we will do is to capture the view hierarchy and try to locate this view in this hierarchy,

  1. Go to Debug menu in Xcode
  2. Select View Debugging option
  3. Select Capture View Hierarchy

It will display details view hierarchy as follows,

While Xcode is displaying this view hierarchy in the left menu bar, filter this hierarchy by entering the view address in the bottom filter menu and it will immediately highlight the view with breaking constraints.

Once you locate this view, you can take the next steps to analyze why constraints are breaking and what you can do to fix them.

2. Through Debug Console

If you want to directly highlight views with breaking constraints for visual inspection, you can even alter their properties on runtime using memory addresses as follows,

In Xcode,

  1. Go to Debug menu
  2. Select Pause

The program will pause and then you can go to debug console and execute the following command,


(lldb) expr ((UIView *)0x7fba00e07c20).backgroundColor = [UIColor purpleColor]

And immediately run,

e (void)[CATransaction flush]

Where,

  • expr is used to evaluate runtime expression in the debugger
  • 0x7fba00e07c20 is the address of view with breaking constraints
  • (UIView *) is used to cast memory address into UIView object
  • Once this address is cast, we can treat it as UIView object and update the property

Please note that sometimes after evaluating UIView subclass appearance from a command line like this won't immediately update the UI. iOS usually waits until the next run loop or any user event such as tap or swipe occurs on the app. You can force the render server to update the display UI with the following command.

e (void)[CATransaction flush]
Even if you are unable to locate faulted view in the debugger, using this trick, you can grab an address of a suspicious view and change its appearance to provide a visual cue on the screen
As you can see I have changed the background color of view for which constraints were breaking and now it appears like this.

To summarize, in order to avoid breaking constraints scenario you can follow some of the tips below

  • Look at individual constraints and check if you have added them correctly
  • Watch out for incomplete and ambiguous constraint rules
  • Use accessbilityIdentifier to uniquely identify views with breaking constraints in the debugger - This will help you easily locate views with breaking constraints by just looking at the debug log
  • Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to pause and catch errors in the debugger
  • This is a generic mistake for any iOS developer - When you create a view and add a constraint to it programmatically, make sure to turn the autoresizing mask off by using [your_view].translatesAutoresizingMaskIntoConstraints = false. (When you add autolayout constraints in the storyboard or xibs, Interface builder will automatically turn it off for you.)
This post summarizes steps I find useful for debugging autolayout issues. If you find any issues with them or have your own tips dealing with autolayout issues, I would love to hear them @jayeshkawli

References:

Debugging iOS AutoLayout Issues - Developing on Staxmanade
This tip may not be news to you, but it was to me so I’ll put this up here to help you, but more to help myself when I get stuck down the road and for...