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

IosSwiftXcode

Ios 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 the console output

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:

enter image description here


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

enter image description here

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.

enter image description here

enter image description here

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionToland HonView Question on Stackoverflow
Solution 1 - IosIrshad MohamedView Answer on Stackoverflow
Solution 2 - IosJalakooView Answer on Stackoverflow
Solution 3 - IosEric AyaView Answer on Stackoverflow
Solution 4 - IosLuca AngelettiView Answer on Stackoverflow
Solution 5 - IosMarco MView Answer on Stackoverflow
Solution 6 - IosJames WolfeView Answer on Stackoverflow
Solution 7 - IosagiraultView Answer on Stackoverflow
Solution 8 - IosAsdrubalView Answer on Stackoverflow
Solution 9 - IosjaroraView Answer on Stackoverflow
Solution 10 - IosAbdelHadyView Answer on Stackoverflow
Solution 11 - IosVasily BodnarchukView Answer on Stackoverflow
Solution 12 - IosOver UkraineView Answer on Stackoverflow
Solution 13 - IosBaseZenView Answer on Stackoverflow
Solution 14 - IoshasayakeyView Answer on Stackoverflow
Solution 15 - IoswlixccView Answer on Stackoverflow
Solution 16 - IosHugo JordaoView Answer on Stackoverflow
Solution 17 - IosTien Nguyen MinhView Answer on Stackoverflow
Solution 18 - IosBomi ChenView Answer on Stackoverflow