Catching unsatisfiable Auto Layout constraints won’t catch 100% of the times the UI breaks, but it can give us a good hint. Unsatisfiable Auto Layout constraints errors happen when there are two or more conflicting Auto Layout constraints installed in the app’s view hierarchy. When that happens, UIKit logs the conflicting constraints in the console, along with the constraint removed by UIKit in an attempt to fix this issue.
We had one more motivation to do this. We can’t ship UI that shows unsatisfiable Auto Layout constraints issues in the logs to our developers (whether those constraints result in a broken UI or not).
I'll be honest. At first, we thought we should just parse the logs from the tests and look for the unsatisfiable Auto Layout constraints issue, but there was this itch that wouldn’t go away to find a better way, and we did.
First, let’s try to make it work in the terminal locally.
Our development setup will be two terminal windows: one for running the UITests in the command line and another for running the LLDB interactive shell.
First, we need to successfully attach the debugger to the UITests from the command line. To do that, we will:
- Run the debugger to wait for the process
- Run the UITests
- Wait for verification
Running the Debugger to Wait for the Process
First, let’s launch the debugger interactive shell. We can do that by running:
From that, we move to the interactive shell:
Now we want to attach it to the app and for that, we need the process ID. Fortunately, we can just use the name to do this. Also, our UITests didn’t run yet, so we want the shell to wait.
The interactive shell should be stopped and waiting as expected.
Run the UI Tests
First of all, make sure you have an unsatisfiable Auto Layout constraints issue.
You will need to run this:
Note: You may need to adjust the OS version depending on what version of Xcode you are using, we are using Xcode 10.1
This will build your UITests target and run it in the terminal.
Wait for Verification
Switch back to the debugger window and you should see this now:
For this, we can use a simple LLDB command:
While we're here, we can add some commands to be executed with that break. Even better, LLDB supports running Python with breakpoints. How cool is that!
Our solution is to add a file on the desktop that we can check on later:
Then we add these three lines:
Then type `DONE` and press return.
Now you should see the app continue execution of the UITests and when your introduced bug happens, you should see something like this in the LLDB window:
Note: By the way, at this point, you can execute any LLDB command just like you do with Xcode, something like this:
We did what we wanted, but in the interactive shell, there’s another way to use LLDB by supplying all the commands that we entered one by one in a text file (“lldb.cmd”?) like this:
Now close the interactive shell and run this in the terminal:
Make sure your tests are running and you will see both executing automatically. Cool, right?
To make sure our tests keep running, we need to:
- Terminate the test
- Re-add the breakpoint
Terminating the Test
To terminate the test, we need to add this to our file:
This will kill our app process and UITests will continue with the next test.
Re-adding the Breakpoint
The script we added to LLDB is just a non-programmable text field so we don’t have loops or anything. However, we can still use loops with recursion.
Remember the LLDB command we used to run the “lldb.cmd” file? We can actually invoke it inside our file and it will do just that. After killing the app, it will start waiting for the process again and add the breakpoint. We need to add this to the file:
command source lldb.cmd
The whole file should look like this:
We use CircleCI, and we need to do two things there:
- Run LLDB in the background.
- Check if there’s a UITests failure which is an unsatisfiable Auto Layout constraints failure.
Run LLDB in the Background
We use CircleCI 2.0 and to run LLDB in the background, we need to add this before running the UITests:
Check if UITests Failure Is an Unsatisfiable Auto Layout Constraints Failure
We need to run that check only if UITests fail to check if that failure is a normal failure or is related to unsatisfiable Auto Layout constraints issues.
I hope you found this useful and that you start using it with your teams. Also, please let me know about your experience with this and share more fun stuff to do with LLDB.
- How We Automate Our iOS Workflow at Instabug Using CircleCI
- How We Refactored Our Monolithic iOS Framework
- Swift 5 Module Stability Workaround for Binary Frameworks
- For more info about using LLDB, check out this tutorial