124
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?

Toland Hon
  • 4,549
  • 2
  • 32
  • 36
  • 10
    You could use `dump`, for example, if the goal is to inspect the dictionary. http://stackoverflow.com/documentation/swift/3966/logging-in-swift/14168/dump – Eric Aya Aug 04 '16 at 17:39
  • 21
    `print(dictionary as! NSDictionary)` cheap trick? – BaseZen Aug 04 '16 at 17:41
  • I really the the dump() suggestion since it doesn't require to write any code or cast it. @EricAya, if you post an answer with that remark, I'll mark it as the answer. – Toland Hon Aug 04 '16 at 18:35
  • 1
    @TolandHon Done. I've made an answer with an example of the output. – Eric Aya Aug 04 '16 at 18:52
  • dump doesn't make it pretty-printed, it gonna looks something like: ▿ 16 key/value pairs #0 ▿ (2 elements) - key: id #1 - super: NSString - super: NSObject - value: 15175100 #2 - super: NSNumber - super: NSValue - super: NSObject ▿ (2 elements) - key: email #3 - super: NSString - super: NSObject - value: america #4 - super: NSMutableString - super: NSString - super: NSObject – Zaporozhchenko Oleksandr May 09 '22 at 11:23

17 Answers17

156

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.

pkamb
  • 33,281
  • 23
  • 160
  • 191
Jalakoo
  • 3,523
  • 2
  • 21
  • 20
  • It prints cyrillic fonts as Unicode numbers `\U0421\U0443\U043f\U0435\U0440` =( – Nike Kov Mar 12 '21 at 09:24
  • *Tip*: make sure to ```import Foundation```. I was playing around with swift on linux and it wont pretty print as shown in the answer without. – Chris Dec 08 '22 at 20:28
155

po solution

For those of you want to see Dictionary as JSON without 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)!)
Irshad Mohamed
  • 2,129
  • 1
  • 18
  • 19
  • 1
    Since is an expression and not an object, it should be 'p' and not 'po'. But thanks very much for this solution! Works fine for me – Alessandro Francucci Jul 06 '18 at 10:41
  • @AlessandroFrancucci does it matter? The command seems to do the same thing either way. – nickjwallin Nov 09 '18 at 20:02
  • Now both way of doing it are working. But before doing a "po print" didn't work for me. (po means print object.... which is a bit confusing if you have a print afterwards and not an object imho) – Alessandro Francucci Nov 12 '18 at 10:08
  • 1
    Awesome! just what I needed to print in a nice way userInfo from PushNotification – Carmen Jun 10 '20 at 13:19
  • 2
    Check [this comment](https://stackoverflow.com/a/63910326/3783833) to leverage this in an lldb alias so you don't have to type it everytime! – agirault Sep 15 '20 at 22:00
  • it's the last force unwrap (`...encoding: .utf8)!)`) that enables the pretty printing. so `encoding: .utf8))` doesn't work. thanks @irshad! – spnkr Jul 19 '23 at 01:12
117

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:.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
  • 11
    This is not pretty printed JSON, this is just dumping a a variable into the console - not valid JSON. While it does suit OP's needs I believe the question needs rewording to match this. – James Wolfe May 16 '18 at 13:54
  • 7
    @JamesWolfe `This is not pretty printed JSON` Nobody said it was. The OP asked about pretty printing Swift dictionaries - nobody is talking about JSON, except a few off-topic answerers. The OP's quesiton is not about JSON at all. – Eric Aya May 16 '18 at 15:34
  • 1
    @JamesWolfe Also please do not change the question. That would be vandalism. The question is clear as it is, and it's not about JSON. Do not change a question just because some answers talk about something else. Thanks. – Eric Aya May 16 '18 at 15:43
39

Just another way using Functional Programming

dictionary.forEach { print("\($0): \($1)") }

Output

B: bravo
A: alfa
F: foxtrot
C: charlie
D: delta
E: echo
Luca Angeletti
  • 58,465
  • 13
  • 121
  • 148
39

For debug purpose only, I would convert the Array or Dictionary to a pretty printed json:

public extension Collection {
    
    /// Returns: the pretty printed JSON string or an error string if any error occur.
    var json: String {
        do {
            let jsonData = try JSONSerialization.data(withJSONObject: self, options: [.prettyPrinted])
            return String(data: jsonData, encoding: .utf8) ?? ""
        } catch {
            return "json serialization error: \(error)"
        }
    }
}

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
}
Marco M
  • 1,215
  • 12
  • 12
15

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.

pkamb
  • 33,281
  • 23
  • 160
  • 191
James Wolfe
  • 340
  • 5
  • 18
  • 1
    Thanks, With this I was able to pretty print amid debugging via `e let jsonData = try! JSONSerialization.data(withJSONObject: response, options: .prettyPrinted);if let jsonString = String(data: jsonData, encoding: .utf8) { print(jsonString) }` – BangOperator Nov 13 '18 at 09:39
  • 1
    This is great! You can leverage this code with an lldb alias to easily compute the json in the debug terminal (details [here](https://stackoverflow.com/a/63910326/3783833)). – agirault Sep 16 '20 at 14:04
  • this is great, thanks!!! – Laura Ramírez Jan 04 '22 at 01:22
14

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"])
agirault
  • 2,509
  • 20
  • 24
  • the `regex` command is in the lldb console (debug console in XCode). If you want to have that command every time you open xcode, you can run the second command in this post in your macOS terminal. I'll update the answer to clarify what terminal should be used for each command. – agirault Aug 20 '21 at 21:09
  • Need to escape the `!` in the bash command – Herno Sep 08 '21 at 01:54
5

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
Asdrubal
  • 2,421
  • 4
  • 29
  • 37
  • 1
    Is there a reason why you made prettyprint a var instead of just a function? – Hayden Holligan Aug 04 '16 at 18:03
  • Honestly, I don't think it matter (I could be wrong). But if you use it a lot then it's less to type in. But raise an interesting question. – Asdrubal Aug 04 '16 at 18:08
  • 3
    Since there's a `description` and `debugDescription` already, it might be more appropriate to call the var `prettyDescription` and return the formatted string. – Toland Hon Aug 04 '16 at 18:43
5

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.

jarora
  • 5,384
  • 2
  • 34
  • 46
4

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)
AbdelHady
  • 9,334
  • 8
  • 56
  • 83
3

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

Vasily Bodnarchuk
  • 24,482
  • 9
  • 132
  • 127
2

swift 5, xcode 10.3:

po print(<your Plist container>)
Over Ukraine
  • 326
  • 1
  • 7
0

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)
BaseZen
  • 8,650
  • 3
  • 35
  • 47
0
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
Toland Hon
  • 4,549
  • 2
  • 32
  • 36
hasayakey
  • 419
  • 3
  • 5
0

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
}
wlixcc
  • 1,132
  • 10
  • 14
0

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)!)
Hugo Jordao
  • 786
  • 8
  • 9
  • 1
    This is great! You can leverage this code with an lldb alias to easily compute the json in the debug terminal (details [here](https://stackoverflow.com/a/63910326/3783833)). – agirault Sep 16 '20 at 14:04
0
 print("{")
    for (key,value) in dictionary {
        if let stringValue = value as? String {
            print("\"\(key)\" = \"\(stringValue.description)\",")
        }else{
            print("\"\(key)\" = \(value),")
        }
    }
    print("}")