Join CTO Moataz Soliman as he explores the potential impact poor performance can have on your bottom line. 👉 Register Today

ebook icon

Engineering

General

Catching Unsatisfiable Auto Layout Constraints in UITests on CircleCI

Mobile app auto layout constraints

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).

‍

How We Catch Unsatisfiable Auto Layout Constraints

‍

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.

UIKit generously offers a symbolic breakpoint UIViewAlertForUnsatisfiableConstraints for this issue and we can use that in our development setup, but what about CI?

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.

‍

1. Attach Debugger to UITests

‍

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.

Our demo app for UITests is called InstabugDemo so we will use it in the examples:


‍

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.

Now, for that we will use xcodebuild, If you are not familiar with it, it is just like running with Xcode except you have to be precise about what you want exactly.

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:


‍

2. Add Break Symbolic Breakpoint

For this, we can use a simple LLDB command:


‍

Now, when that exception is fired from UIKit, your interactive shell will stop just like it does with Xcode and you can add any LLDB commands you want to them. However, we are building for CI so we don’t want to do things ourselves.

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.

‍

3. Continue Running the UITests

‍

Just enter:


‍

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:


‍

4. Automating LLDB

‍

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?

‍

5. Making Sure Your Tests Keep Running

‍

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:


‍

6. Adding it to Circle CI

‍

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:


‍

The key here is adding background: true which will make it return right away but continue to run in the background.

‍

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.


‍

when: on_fail will make sure it runs on failure only.

‍

Conclusion

‍

That’s it!

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.

Seeing is Believing, Start Your 14-Day Free Trial

In less than a minute, integrate the Instabug SDK for iOS, Android, React Native, Xamarin, Cordova, Flutter, and Unity mobile apps