Is there a way to pretty print Swift dictionaries to the console?
IosSwiftXcodeIos Problem Overview
NSDictionary *dictionary = @{@"A" : @"alfa",
@"B" : @"bravo",
@"C" : @"charlie",
@"D" : @"delta",
@"E" : @"echo",
@"F" : @"foxtrot"};
NSLog(@"%@", dictionary.description);
prints out the following on the console:
{
A = alfa;
B = bravo;
C = charlie;
D = delta;
E = echo;
F = foxtrot;
}
let dictionary: [String : String] = ["A" : "alfa",
"B" : "bravo",
"C" : "charlie",
"D" : "delta",
"E" : "echo",
"F" : "foxtrot"];
print(dictionary)
prints out the following on the console:
["B": "bravo", "A": "alfa", "F": "foxtrot", "C": "charlie", "D": "delta", "E": "echo"]
Is there a way in Swift to get it to pretty print dictionaries where each key-value pair occupies a new line?
Ios Solutions
Solution 1 - Ios
po solution
For those of you want to see Dictionary as JSON with out escape sequence in console, here is a simple way to do that
(lldb)p print(String(data: try! JSONSerialization.data(withJSONObject: object, options: .prettyPrinted), encoding: .utf8 )!)
Update
Check this answer too Answer
Solution 2 - Ios
Casting a dictionary to 'AnyObject' was the simplest solution for me:
let dictionary = ["a":"b",
"c":"d",
"e":"f"]
print("This is the console output: \(dictionary as AnyObject)")
This is easier to read for me than the dump option, but note it won't give you the total number of key-values.
Solution 3 - Ios
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:
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:
.
Solution 4 - Ios
Just another way using Functional Programming
dictionary.forEach { print("\($0): \($1)") }
Output
B: bravo
A: alfa
F: foxtrot
C: charlie
D: delta
E: echo
Solution 5 - Ios
For debug purpose only I would convert the Array or Dictionary to a pretty printed json:
public extension Collection {
/// Convert self to JSON String.
/// Returns: the pretty printed JSON string or an empty string if any error occur.
func json() -> String {
do {
let jsonData = try JSONSerialization.data(withJSONObject: self, options: [.prettyPrinted])
return String(data: jsonData, encoding: .utf8) ?? "{}"
} catch {
print("json serialization error: \(error)")
return "{}"
}
}
}
Then:
print("\nHTTP request: \(URL)\nParams: \(params.json())\n")
Result on console:
HTTP request: https://example.com/get-data
Params: {
"lon" : 10.8663676,
"radius" : 111131.8046875,
"lat" : 23.8063882,
"index_start" : 0,
"uid" : 1
}
Solution 6 - Ios
I wouldn't consider a lot of the answers provided here true pretty printed JSON, as when you pass the results into a JSON validator the result is invalid (often due to the code including '=' rather than ':').
The easiest way I've found of doing this is just converting the JSON object to data using the pretty printed writing option then printing a string using the resulting data.
Here is an example:
let jsonData = try! JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
if let jsonString = String(data: jsonData, encoding: .utf8) {
print(jsonString)
}
Result:
{
"jsonData": [
"Some String"
],
"moreJSONData": "Another String",
"evenMoreJSONData": {
"A final String": "awd"
}
}
EDIT: It's been pointed out that the OP did not ask for JSON, however I find that the answers that recommend just printing or dumping the data into the console provide very little formatting (if any) and are therefore not pretty printing.
I believe that despite the OP not asking for JSON, it is a viable answer as it is a much more readable format for data than the horrendous format that is spat out into the console by xcode/swift.
Solution 7 - Ios
Adjusted based on my other answer here.
PrettyPrint JSON solution using LLDB alias
✨No code needed ✨
- To get a nice json formatting (indentations, newlines, etc) you can define an lldb alias by running this command in your lldb terminal in XCode (source):
command regex pjson 's/(.+)/expr print(NSString(string: String(data: try! JSONSerialization.data(withJSONObject: %1, options: .prettyPrinted), encoding: .utf8)!))/'
- The command above only works during the current XCode session. To avoid re-defining the alias everytime you open XCode, run the following command in your macOS terminal. It will append the alias defined above to
~/.lldbinit
which LLDB will load everytime XCode starts:
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:
// Using Any? to demo optional & arbitrary Type
let dictionary: Any? = [
"embedded": [
"JustForTheSakeOfTheDemo": 42
],
"A" : "alfa",
"B" : "bravo",
"C" : "charlie",
"D" : "delta",
"E" : "echo",
"F" : "foxtrot"
]
✅ Output of pjson dictionary
{
"F" : "foxtrot",
"D" : "delta",
"embedded" : {
"JustForTheSakeOfTheDemo" : 42
},
"E" : "echo",
"A" : "alfa",
"C" : "charlie",
"B" : "bravo"
}
❌ Output of p dictionary
(Any?) $R0 = 7 key/value pairs {
[0] = {
key = "F"
value = "foxtrot"
}
[1] = {
key = "D"
value = "delta"
}
[2] = {
key = "embedded"
value = 1 key/value pair {
[0] = (key = "JustForTheSakeOfTheDemo", value = 42)
}
}
[3] = {
key = "E"
value = "echo"
}
[4] = {
key = "A"
value = "alfa"
}
[5] = {
key = "C"
value = "charlie"
}
[6] = {
key = "B"
value = "bravo"
}
}
❌ Output of p (dictionary as! NSDictionary)
(NSDictionary) $R18 = 0x0000000281e89710 {
ObjectiveC.NSObject = {
base__SwiftNativeNSDictionaryBase@0 = {
baseNSDictionary@0 = {
NSObject = {
isa = Swift._SwiftDeferredNSDictionary<Swift.String, Any> with unmangled suffix "$"
}
}
}
}
}
❌ Output of po dictionary
▿ Optional<Any>
▿ some : 7 elements
▿ 0 : 2 elements
- key : "F"
- value : "foxtrot"
▿ 1 : 2 elements
- key : "D"
- value : "delta"
▿ 2 : 2 elements
- key : "embedded"
▿ value : 1 element
▿ 0 : 2 elements
- key : "JustForTheSakeOfTheDemo"
- value : 42
▿ 3 : 2 elements
- key : "E"
- value : "echo"
▿ 4 : 2 elements
- key : "A"
- value : "alfa"
▿ 5 : 2 elements
- key : "C"
- value : "charlie"
▿ 6 : 2 elements
- key : "B"
- value : "bravo"
❌ Output of po print(dictionary)
Optional(["F": "foxtrot", "D": "delta", "embedded": ["JustForTheSakeOfTheDemo": 42], "E": "echo", "A": "alfa", "C": "charlie", "B": "bravo"])
Solution 8 - Ios
You can just use a for loop and print each iteration
for (key,value) in dictionary {
print("\(key) = \(value)")
}
Application in extension:
extension Dictionary where Key: CustomDebugStringConvertible, Value:CustomDebugStringConvertible {
var prettyprint : String {
for (key,value) in self {
print("\(key) = \(value)")
}
return self.description
}
}
Alternate application:
extension Dictionary where Key: CustomDebugStringConvertible, Value:CustomDebugStringConvertible {
func prettyPrint(){
for (key,value) in self {
print("\(key) = \(value)")
}
}
}
Usage:
dictionary.prettyprint //var prettyprint
dictionary.prettyPrint //func prettyPrint
Output (Tested in Xcode 8 beta 2 Playground):
A = alfa
B = bravo
C = charlie
D = delta
E = echo
F = foxtrot
Solution 9 - Ios
The methodology of converting the Swift Dictionary to json and back is the neatest. I use Facebook's chisel which has a pjson command to print a Swift dictionary. Eg:
(lldb) pjson dict as NSDictionary
This should pretty-print the dictionary. This is a much cleaner way to do what has already been suggested. P.S. For now, you'll have to cast dict as NSDictionary because Objective-C runtime doesn't understand Swift dictionaries. I have already raised a PR on chisel to get rid of that restriction.
UPDATE: My PR got accepted. Now you can use psjson command instead of pjson mentioned above.
Solution 10 - Ios
For Swift 3 (& building on the brilliant answer by @Jalakoo), make the following Dictionary
extension:
extension Dictionary where Key: ExpressibleByStringLiteral, Value: Any {
var prettyPrint: String {
return String(describing: self as AnyObject)
}
}
then print a dictionary of any hierarchy in a pretty way (better than dump()
) using this:
print(dictionary!.prettyPrint)
Solution 11 - Ios
Details
- Xcode 10.2.1 (10E1001), Swift 5
Solution
extension Dictionary {
func format(options: JSONSerialization.WritingOptions) -> Any? {
do {
let jsonData = try JSONSerialization.data(withJSONObject: self, options: options)
return try JSONSerialization.jsonObject(with: jsonData, options: [.allowFragments])
} catch {
print(error.localizedDescription)
return nil
}
}
}
Usage
let dictionary: [String : Any] = [
"id": 0,
"bool": true,
"int_array": [1,3,5],
"dict_array": [
["id": 1, "text": "text1"],
["id": 1, "text": "text2"]
]
]
print("Regualr print:\n\(dictionary)\n")
guard let formatedDictionary = dictionary.format(options: [.prettyPrinted, .sortedKeys]) else { return }
print("Pretty printed:\n\(formatedDictionary)\n")
Results
Solution 12 - Ios
swift 5, xcode 10.3:
po print(<your Plist container>)
Solution 13 - Ios
How about:
import Foundation
extension Dictionary {
var myDesc: String {
get {
var v = ""
for (key, value) in self {
v += ("\(key) = \(value)\n")
}
return v
}
}
}
// Then, later, for any dictionary:
print(dictionary.myDesc)
Solution 14 - Ios
extension String {
var conslePrintString: String {
guard let data = "\""
.appending(
replacingOccurrences(of: "\\u", with: "\\U")
.replacingOccurrences(of: "\"", with: "\\\"")
)
.appending("\"")
.data(using: .utf8) else {
return self
}
guard let propertyList = try? PropertyListSerialization.propertyList(from: data,
options: [],
format: nil) else {
return self
}
guard let string = propertyList as? String else {
return self
}
return string.replacingOccurrences(of: "\\r\\n", with: "\n")
}
}
let code in extension String and it works fine
let string = "\(jsonDictionary)".conslePrintString
Solution 15 - Ios
When debugging, output the struct that conform Codable Protocol to the console
use json format.
extension Encodable {
var jsonData: Data? {
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
return try? encoder.encode(self)
}
}
extension Encodable where Self: CustomDebugStringConvertible {
var debugDescription: String {
if let data = self.jsonData,
let string = String(data: data, encoding: .utf8) {
return string
}
return "can not convert to json string"
}
}
strcut conform CustomDebugStringConvertible
struct Test: Codable, CustomDebugStringConvertible {
let a: String
let b: Int
}
let t = Test(a: "test string", b: 30)
debug print struct
(lldb) p print(t)
{
"a" : "test string",
"b" : 30
}
Solution 16 - Ios
Pretty print from Data object:
let jsonObj = try JSONSerialization.jsonObject(with: data, options: [])
let jsonData = try JSONSerialization.data(withJSONObject: jsonObj, options: [.prettyPrinted])
print(String(data: jsonData, encoding: .utf8)!)
Solution 17 - Ios
print("{")
for (key,value) in dictionary {
if let stringValue = value as? String {
print("\"\(key)\" = \"\(stringValue.description)\",")
}else{
print("\"\(key)\" = \(value),")
}
}
print("}")
Solution 18 - Ios
language: Objective-C
> NSString+PrettyPrint.h
@interface NSString (PrettyPrint)
+ (NSString * _Nonnull)prettifiedJsonStringFromData:(nullable NSData *)data;
+ (NSString * _Nonnull)prettifiedStringFromDictionary:(nullable NSDictionary *)dictionary;
@end
> NSString+PrettyPrint.m
#import "NSString+PrettyPrint.h"
@implementation NSString (Print)
+ (NSString *)prettifiedStringFromDictionary:(nullable NSDictionary *)dictionary {
if (dictionary == nil) { return @"nil"; }
NSMutableString *returnStr = [NSMutableString stringWithString:@"[ \n"];
for (NSString *key in dictionary) {
[returnStr appendFormat:@" %@: %@,\n", key, [dictionary valueForKey:key]];
}
[returnStr appendFormat:@"]"];
return returnStr;
}
+ (NSString *)prettifiedJsonStringFromData:(nullable NSData *)data {
if (data == nil) { return @"nil"; }
NSData *jsonData;
NSError *error = nil;
NSString *jsonStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
jsonData = [jsonStr dataUsingEncoding:NSUTF8StringEncoding];
id jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingAllowFragments error:&error];
if (jsonObject == nil) {
return @"nil (json object from data)";
} else {
BOOL isValidJsonObject = [NSJSONSerialization isValidJSONObject:jsonObject];
if (isValidJsonObject) {
NSData *finalData = [NSJSONSerialization dataWithJSONObject:jsonObject options:NSJSONWritingPrettyPrinted error:&error];
//TODO: error description
NSString *prettyJson = [[NSString alloc] initWithData:finalData encoding:NSUTF8StringEncoding];
return prettyJson;
} else {
return [NSString stringWithFormat:@"%@\n%@", jsonStr, @" (⚠️ Invalid json object ⚠️)\n"];
}
}
}
@end
then call methods when u need to use them.
ex1. Print NSData for body, response ...etc
NSLog(@"body: %@", [NSString prettifiedJsonStringFromData:[request HTTPBody]]);
ex2. Print NSDictionary
NSLog(@"headers: %@", [NSString prettifiedStringFromDictionary:[request allHTTPHeaderFields]]);
Probably you'll get these results in log.