How to Pretty Print Swift Dictionaries to the Console

Is there a way to pretty print Swift dictionaries to the console?

You could use dump, for example, if the goal is to inspect the dictionary. dump is part of Swift's standard library.

Usage:

let dictionary: [String : String] = ["A" : "alfa",
"B" : "bravo",
"C" : "charlie",
"D" : "delta",
"E" : "echo",
"F" : "foxtrot"]

dump(dictionary)

Output:

Sample Image


dump prints the contents of an object via reflection (mirroring).

Detailed view of an array:

let names = ["Joe", "Jane", "Jim", "Joyce"]
dump(names)

Prints:

▿ 4 elements

- [0]: Joe

- [1]: Jane

- [2]: Jim

- [3]: Joyce

For a dictionary:

let attributes = ["foo": 10, "bar": 33, "baz": 42]
dump(attributes)

Prints:

▿ 3 key/value pairs

▿ [0]: (2 elements)

- .0: bar

- .1: 33

▿ [1]: (2 elements)

- .0: baz

- .1: 42

▿ [2]: (2 elements)

- .0: foo

- .1: 10

dump is declared as dump(_:name:indent:maxDepth:maxItems:).

The first parameter has no label.

There's other parameters available, like name to set a label for the object being inspected:

dump(attributes, name: "mirroring")

Prints:

▿ mirroring: 3 key/value pairs

▿ [0]: (2 elements)

- .0: bar

- .1: 33

▿ [1]: (2 elements)

- .0: baz

- .1: 42

▿ [2]: (2 elements)

- .0: foo

- .1: 10

You can also choose to print only a certain number of items with maxItems:, to parse the object up to a certain depth with maxDepth:, and to change the indentation of printed objects with indent:.

Prettier debug output printing Swift Dictionary in Xcode

⚠️ The (previously) accepted answer only provided the dictionary as a non-formatted single line string like so:

Optional(["transactionId": 333, "customerId": 111, "extraId": 444])

➡️ As soon as you get more keys and embedded objects/dictionaries it becomes difficult to read.



PrettyPrint JSON solution

  • To get a nice json formatting (indentations, newlines, etc) you can define an alias (I named mine pjson) by running this command in your lldb terminal (source):
command regex pjson 's/(.+)/expr print(NSString(string: String(data: try! JSONSerialization.data(withJSONObject: %1, options: .prettyPrinted), encoding: .utf8)!))/'
  • You probably don't want to re-define the alias everytime you open Xcode, so run the following command to append the alias definition to ~/.lldbinit:
echo "command regex pjson 's/(.+)/expr print(NSString(string: String(data: try! JSONSerialization.data(withJSONObject: %1, options: .prettyPrinted), encoding: .utf8)!))/'" >> ~/.lldbinit
  • This will create the pjson alias which you can use in your lldb terminal in Xcode:
pjson object


Comparing the outputs for the following Swift object:

let object: Any? = [
"customerId": 111,
"transactionId": 333,
"extraId": 444,
"embeddedDict": [
"foo": true
]
]

❌ Output of po print(data)

Optional(["transactionId": 333, "embeddedDict": ["foo": true], "customerId": 111, "extraId": 444])

✅ Output of pjson

{
"transactionId" : 333,
"embeddedDict" : {
"foo" : true
},
"customerId" : 111,
"extraId" : 444
}

Printing a dictionary value in Swift

If you know that the key is there, you can print the value with an exclamation point:

var airports = ["ALB":"Albany International", "ORD": "O'Hare"]
println(airports["ALB"]) // Prints Optional("Albany International")
println(airports["ALB"]!) // Prints Albany International

If you are not sure that the key is there, and you would like to avoid an error, you can do this:

if let alb = airports["ALB"] {
print(alb)
}

Function print will be called only when "ALB" key is present in the dictionary, in which case alb would be assigned a non-optional String.

NSDictionary-like pretty-print in the debugger (or log)

TL;DR;

Use NSContainers-PrettyPrint and carefully read the docs.

Long Answer

After much more searching I discovered the descriptionWithLocale:indent: method. As documented, I should've been able to implement this in my own classes to achieve the desired pretty-print formatting. However, after some failed attempts I found a similar SO question. Turns out descriptionWithLocale:indent: only works if you subclass a Foundation container class because of "security concerns".

Unsatisfied with that approach I continued digging and found this radar but also a solution in NSContainers-PrettyPrint. After some trial and error I got things working decently well. (It's not on CocoaPods so you have to add it manually).

Once NSContainers-PrettyPrint is added you'll probably want JRSwizzle too. Then define DEBUGPRINT_ALL and DEBUGPRINT_SWIZZLE in your DEBUG targets preprocessor macros. Finally, you can implement your descriptionWithLocale:indent: methods in terms of the fs_* helpers and best practices.

Using the same Foo from my question as an example

@implementation Foo
- (NSString*)description
{
return [NSString stringWithFormat:@"%@ %@", super.description, self.dict.description];
}

- (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level
{
NSString * indent = [NSString fs_stringByFillingWithCharacter:' ' repeated:fspp_spacesPerIndent*level];
NSMutableString * str = [[NSMutableString alloc] init];
[str fs_appendObjectStartWithIndentString:indent caller:self];
[str appendString:[self.dict descriptionWithLocale:locale indent:level+1]];
[str fs_appendObjectEnd];
return str;
}
@end

Would produce the following output given the same f0, f1 and f2 instances

(lldb) po f0
<Foo: 0x8a385c0> {
value = 0;
next = <null>;
}
(lldb) po f1
<Foo: 0x8a38630> {
value = 1;
next = <Foo:0x8a385c0 {
value = 0;
next = <null>;
}>;
}
(lldb) po f2
<Foo: 0x8a38660> {
value = 2;
next = <Foo:0x8a38630 {
value = 1;
next = <Foo:0x8a385c0 {
value = 0;
next = <null>;
}>;
}>;
}

The above descriptionWithLocale:indent: could use some tweaking to reduce the excessive whitespace but it still beats the alternatives.

How can I read my objects in XCode lldb as first state JSON in Swift?

You can use this extension to convert it from dictionary to JSON and by using it you can print it in the console.

extension Dictionary {
var json: String {
let invalidJson = "Not a valid JSON"
do {
let jsonData = try JSONSerialization.data(withJSONObject: self, options: .prettyPrinted)
return String(bytes: jsonData, encoding: String.Encoding.utf8) ?? invalidJson
} catch {
return invalidJson
}
}

func printJson() {
print(json)
}
}

suppose you have yourDicObj decoded from JSON and you want to see it as JSON before decoded on the console

usage in console :

lldb po yourDicObj.printJson

it will print as JSON

Is there a better way to Pretty Print the json string than I'm using

A slightly prettier version of what you have:

if let json = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers),
let jsonData = try? JSONSerialization.data(withJSONObject: json, options: .prettyPrinted) {
print(String(decoding: jsonData, as: UTF8.self))
} else {
print("json data malformed")
}

How can we format or convert the Xcode log or print in JSON format?

There is a very simpler way in swift
use need to use this pod: SwiftyJSON

Install the pod and use the following code:

import SwiftyJSON

func printInJSON {
var dictionary: [String : Any] = [:]
dictionary["build_number"] = "1.0"
dictionary["data"] = "Print in JSON format on iOS xcode"

// SwiftyJson magic
let json = JSON(dictionary)
print(json)
}

Output:
{
"build_number" : "1.0"
"data" : "Print in JSON format on iOS xcode"
}

How to create JSON from a dictionary in Swift 4?

After trying out various ways, the below way is what worked for me for getting the exact format required by the backend.

var messageDictionary = [
"sender":"system1@example.com",
"recipients":[
"system2@example.com"
],
"data":[
"text" : data
]
] as [String : Any]

let jsonData = try! JSONSerialization.data(withJSONObject: messageDictionary)
let jsonString = NSString(data: jsonData, encoding: String.Encoding.utf8.rawValue)


Related Topics



Leave a reply



Submit