You are currently viewing Adding Swift Code as Custom LLDB Command

Adding Swift Code as Custom LLDB Command

If I ask which LLDB command you used the most? I am sure most iOS developers will answer po. But did you know that you can actually define your own custom LLDB command using purely Swift code?

In this article, I would like to show you what it takes to create your own LLDB command. Here are what I will be covering:

  • Adding your first LLDB command
  • Adding LLDB command with arguments
  • Converting complex Swift code to LLDB command

Without further ado, let’s get started!


Adding Your First LLDB Command

Understanding the LLDB Command Structure

In order to add a custom LLDB command, we must leverage the command alias LLDB command. It has the following structure:

command alias [command_name] expr -l Swift -O -- [swift_code]

Let’s break down the above command in details:

  • command alias: The LLDB command that alias the Swift code with a name
  • [command_name]: The custom command name
  • expr -l Swift -O --: Ask the LLDB debugger to interpret everything that follows as Swift code
  • [swift_code]: The Swift code that defines the custom command logic

For example, if we want to add a custom command named greet that prints a “Hello World!” statement at the console, the LLDB command will be something like this:

command alias greet expr -l Swift -O -- print("Hello World!")

Adding the Custom Command

Now that we have constructed the alias command for greet, it is time to add it to the LLDB debugger.

The most straightforward way to add the greet command to the LLDB debugger is to execute the alias command in the Xcode console.

Execute LLDB alias command in Xcode console
Execute alias command in Xcode console

However, doing this only makes the greet command available in that particular debugging session. In order words, we will need to retype the same alias command every time we start a new debugging session.

In order to avoid that from happening, we can leverage the .lldbinit file located in the home directory. Note that it is a hidden file, if you can’t see the file, you can use the following shortcut to show the hidden files in your finder:

shift + command + .

Once you have enabled finder to show hidden files, but you still not able to locate the file, go ahead and create one in your home directory using the following terminal command:

touch ~/.lldbinit

After that, open the file and paste the entire alias command into the .lldbinit file you just created. With that, Xcode will execute our alias command every time it starts a new debugging session.

Pro Tip:

If you do not want to restart your debugging session every time you have updated the .lldbinit file, you can reload it using the following command:

command source ~/.lldbinit


Adding LLDB Command with Arguments

In this section, let’s take things one step further by adding a command that is able to accept an argument. For demonstration purposes, let’s modify our greet command so that it is able to accept a string and print out the greeting message accordingly.

This time, we will leverage the command regex LLDB command. It has the following structure:

command regex [command_name] 's/[regex]/expr -l Swift -O -- [swift_code]/'

I won’t go into too much detail on how the regex command works, as it is beyond the scope of this article. Generally, all you need to do is to replace [regex] with the regular expression statement (.+), and then use %1 to represent the argument in the Swift code.

With that in mind, we can update the greet command accordingly like so:

command regex greet 's/(.+)/expr -l Swift -O -- print("Hello \(%1)!")/'

Here’s the greet command in action (assuming name = "Swift Senpai"):

(lldb) greet name
Hello Swift Senpai!

At this stage, you might ask: what if I need to pass in more than 1 argument? The answer is actually pretty simple.

First, append more (.+) to the regex statement and separate each (.+) with a whitespace. After that, use %2, %3, %4… to represent each subsequence argument in the Swift code.

Now, let’s try to adjust our greet command to accept 2 arguments:

command regex greet 's/(.+) (.+)/expr -l Swift -O -- print("Hello (%1) and (%2)!")/'

To use the command, just separate each argument with whitespace like so (assuming name1 = "Swift Senpai" and name2 = "iOS developers"):

(lldb) greet name1 name2
Hello Swift Senpai and iOS developers!

Now that you have seen how to add a custom LLDB command that takes multiple arguments. In the next section, I will show you how to convert a multi-line Swift function into a custom LLDB command.


Converting Complex Swift Code to LLDB Command

One caveat of adding custom Swift code as LLDB command is that everything must be done in one single line. Therefore, if we have a multi-line Swift function, we must first convert it into a single line, then only we can add it to the .lldbinit file.

Let’s say we want to add the following Swift function that converts RGB value to hex value:

func hex(r: Int, g: Int, b: Int) {

    /* Make sure RGB value within range */
    if (r >= 0 && r <= 255) && (g >= 0 && g <= 255) && (b >= 0 && b <= 255) {

        let rgb:Int = r<<16 | g<<8 | b<<0
        let hex = String(format:"#%06x", rgb)

        print(hex)
    } else {
        print("Invalid input value")
    }
}

Notice how I use the /* */ and not the // syntax for code comment, this is to ensure that our Swift code will not break after we convert it into a single line later on.

On top of that, we also need to make a few adjustments to our Swift code before we can convert it into a single line. Here are what we need to do:

  1. Define a variable for each function parameter.
  2. Assign %1, %2, %3… to each defined variable.
  3. Add ; at the end of each statement.

This is how our Swift code will looks like after the adjustments:

let r = %1;
let g = %2;
let b = %3;

/* Make sure RGB value within range */
if (r >= 0 && r <= 255) && (g >= 0 && g <= 255) && (b >= 0 && b <= 255) {

    let rgb:Int = r<<16 | g<<8 | b<<0;
    let hex = String(format:"#%06x", rgb);

    print(hex);
} else {
    print("Invalid input value");
}

With that, we can proceed to convert our Swift code into a single line. I personally like to use this free online tool for single-line conversion. However, if you know of any great tools out there that you would like to recommend, feel free to let me know.

Once we converted our Swift code into a single line, we can construct the regex command like so:

command regex hex 's/(.+) (.+) (.+)/expr -l Swift -O -- let r = %1; let g = %2; let b = %3; if (r >= 0 && r <= 255) && (g >= 0 && g <= 255) && (b >= 0 && b <= 255) { let rgb:Int = r<<16 | g<<8 | b<<0; let hex = String(format:"#%06x", rgb); print(hex); } else { print("Invalid input value"); }/'

Go ahead and paste the command into the .lldbinit file, then we are good to go.

Convert RGB to hex value using custom LLDB command in Xcode console
Convert RGB to hex value using custom LLDB command

Practical Custom LLDB Commands

Now that you have learned how to add a custom command to your LLDB debugger, what custom LLDB command should you add then?

I personally find the following custom command especially useful. It is a command that pretty prints any JSON serializable type such as Dictionary, Array, Data, etc as JSON string in the Xcode console. You can find out more here.

In addition, I also like the series of custom commands discussed in this article, where we can use them to modify the color of the UI elements on the fly without having to rebuild the project.


Wrapping Up

This article barely scratches the surface of the LLDB debugger capabilities. If you are new to LLDB, I hope this article can inspire you to start exploring this amazing debugging tool starting from today.

If you enjoy reading this article, feel free to check out my other articles related to testing and Xcode. You can also follow me on Twitter, and subscribe to my monthly newsletter.

Thanks for reading. 👨🏻‍💻


👋🏻 Hey!

While you’re still here, why not check out some of my favorite Mac tools on Setapp? They will definitely help improve your day-to-day productivity. Additionally, doing so will also help support my work.

  • Bartender: Superpower your menu bar and take full control over your menu bar items.
  • CleanShot X: The best screen capture app I’ve ever used.
  • PixelSnap: Measure on-screen elements with ease and precision.
  • iStat Menus: Track CPU, GPU, sensors, and more, all in one convenient tool.