Advanced Xcode debugging: Creating an lldb alias for printing dictionaries as formatted JSON

Axel Ancona Esselmann
3 min readAug 2, 2018

--

The Xcode console gives you access to a powerful scriptable debugger: LLDB. One of the first LLDB commands I learned was po, which prints out the description of an Objective c or Swift object. I often find myself needing to display a Swift dictionary as a valid JSON string. Before I knew about LLDB I would write a couple of lines of code for encoding a dictionary to JSON, add a print statement, set a break point, recompile, copy the string from the console and then go on to remove escape characters to obtain valid JSON. Not OK. Let’s iterate through the process of streamlining this process.

Ditch print() statements

Since we have breakpoints and the ability to print application state to the console with po, there is really no need for littering the code with print() statements. Many times I have accidentally checked in a stray print() statements.

Ditch the recompile

LLDB is not just for printing out variables. You can write code to format the output.
Let’s say you have a Swift dictionary jsonDict that you would like to print out as a JSON string. Lets set the breakpoint and use po to do that:

po NSString(string: String(data: try! JSONSerialization.data(withJSONObject: jsonDict, options: .prettyPrinted), encoding: .utf8)!)

Awesome! No more recompiling for converting a dictionary to JSON when debugging. But we can do better! LLDB allows us to create aliases.

Alias!

If you follow an LLDB tutorial like this one you might thing the following should work:

command alias poj po NSString(string: String(data: try! JSONSerialization.data(withJSONObject: json, options: .prettyPrinted), encoding: .utf8)!)

This is when we learn that po itself is an alias (for expression -O -l swift — -) and apparently nesting aliases is not allowed. Let’s try again:

command alias poj expression -O -l swift - - NSString(string: String(data: try! JSONSerialization.data(withJSONObject: jsonDict, options: .prettyPrinted), encoding: .utf8)!)

That works. We can now set a breakpoint and print out a JSON dictionary named jsonDict. But what if the dictionary has a different name? When working with aliases we should be able to work with parameters, but for some reason we can’t use them when printing to the console.
We can save the alias:

command alias poj expression -O -l swift - - NSString(string: String(data: try! JSONSerialization.data(withJSONObject: %1, options: .prettyPrinted), encoding: .utf8)!)

but when we try to use it:


poj jsonDict

we get the following error:

error: <EXPR>:3:75: error: ‘%’ is not a prefix unary operator

This is where it gets weired. The workaround requires us to store our expression as a regular expression:

command regex poj 's/(.+)/expr NSString(string: String(data: try! JSONSerialization.data(withJSONObject: %1, options: .prettyPrinted), encoding: .utf8)!)/'

Now we can finally do use our alias that accepts the name of the dictionary as a parameter, but there are two more issues:

The JSON string is escapded!

Let’s fix that by adding print():

command regex poj 's/(.+)/expr print(NSString(string: String(data: try! JSONSerialization.data(withJSONObject: %1, options: .prettyPrinted), encoding: .utf8)!))/'

and voila, we have a butifully formatted JSON string. Done!
Until you close Xcode, try to use your alias and get the following:

error: 'poj' is not a valid command.
error: Unrecognized command 'poj'.

Load you aliases when starting Xcode

There is one last step left and you will be in dictionary JSON heaven. Create a file named .lldbinit in your user directory and add your alias to it. Files that start with a period are hidden files so either enable viewing of hidden files for Finder or use the command line to edit this file.

Now you are able to close Xcode and you alias will await you when you open Xcode again.

Revisited:

Thanks to André Pinto for suggesting sorting the JSON keys by passing in .prettyPrinted into the JSONSerialization options :

command regex poj 's/(.+)/expr print(NSString(string: String(data: try! JSONSerialization.data(withJSONObject: %1, options: [.prettyPrinted, .sortedKeys]), encoding: .utf8)!))/'

I also found myself frequently wanting to turn Encodable instances into formatted JSON. I created poe for that purpose:

command regex poe 's/(.+)/expr print(NSString(string: String(data: try! JSONSerialization.data(withJSONObject: (try! JSONSerialization.jsonObject(with: try! JSONEncoder().encode(%1))), options: .prettyPrinted), encoding: .utf8)!))/'

--

--

Axel Ancona Esselmann

Senior IOS engineer at 23andMe, developed and teaches course on IOS engineering at San Francisco State University