Swift
pretty print
dictionaries
console output
Swift programming

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

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

When debugging Swift applications, you often need to inspect the contents of a dictionary. The default print() output crams everything onto a single line, making it nearly impossible to read for any non-trivial data structure. Swift provides several approaches to produce formatted, human-readable dictionary output, ranging from built-in functions to JSON serialization techniques. Knowing which method to use in each situation will make your debugging workflow significantly faster.

The Problem with Default print()

By default, print() outputs a dictionary as a single-line dump with no indentation:

swift
1let user: [String: Any] = [
2    "name": "Alice",
3    "age": 30,
4    "roles": ["admin", "editor"],
5    "address": ["city": "Seattle", "zip": "98101"]
6]
7
8print(user)
9// Output: ["name": "Alice", "age": 30, "roles": ["admin", "editor"], "address": ["city": "Seattle", "zip": "98101"]]

For small dictionaries this is workable, but once you have nested structures or more than a few keys, the output becomes a wall of text.

Using dump()

Swift's built-in dump() function prints a structured, indented representation of any value. It uses Mirror reflection under the hood, so it works with all Swift types:

swift
dump(user)

The output looks like:

 
14 key/value pairs
2   (2 elements)
3    - key: "name"
4    - value: "Alice"
5   (2 elements)
6    - key: "age"
7    - value: 30
8   (2 elements)
9    - key: "roles"
10    ▿ value: 2 elements
11      - "admin"
12      - "editor"
13  ...

The advantage of dump() is that it requires no setup and handles any type. The downside is that the output format uses reflection markers rather than standard data notation, which can be harder to scan visually.

Using JSONSerialization with .prettyPrinted

For dictionaries that contain only JSON-compatible types (String, Int, Double, Bool, Array, Dictionary, or NSNull), you can serialize them to formatted JSON:

swift
1func prettyPrint(_ dictionary: [String: Any]) {
2    if let data = try? JSONSerialization.data(
3        withJSONObject: dictionary,
4        options: [.prettyPrinted, .sortedKeys]
5    ) {
6        if let string = String(data: data, encoding: .utf8) {
7            print(string)
8        }
9    }
10}
11
12prettyPrint(user)

This produces clean, indented JSON output:

json
1{
2  "address" : {
3    "city" : "Seattle",
4    "zip" : "98101"
5  },
6  "age" : 30,
7  "name" : "Alice",
8  "roles" : [
9    "admin",
10    "editor"
11  ]
12}

The .sortedKeys option ensures consistent ordering, which is helpful when comparing output across runs. This approach only works with JSON-compatible types. If your dictionary contains custom objects or non-bridgeable Swift types, JSONSerialization will throw an error.

Using JSONEncoder with Codable Types

When your data model conforms to Codable, JSONEncoder provides a type-safe alternative:

swift
1struct User: Codable {
2    let name: String
3    let age: Int
4    let roles: [String]
5}
6
7let codableUser = User(name: "Alice", age: 30, roles: ["admin", "editor"])
8
9let encoder = JSONEncoder()
10encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
11
12if let data = try? encoder.encode(codableUser),
13   let string = String(data: data, encoding: .utf8) {
14    print(string)
15}

This approach is preferred for structured model objects because the compiler verifies the data types at compile time, eliminating runtime serialization failures.

Using CustomStringConvertible

For custom types that you print frequently, conforming to CustomStringConvertible lets you define exactly how they appear in print() calls:

swift
1struct Config: CustomStringConvertible {
2    let settings: [String: String]
3
4    var description: String {
5        let lines = settings.sorted(by: { $0.key < $1.key })
6            .map { "  \($0.key): \($0.value)" }
7            .joined(separator: "\n")
8        return "Config {\n\(lines)\n}"
9    }
10}
11
12let config = Config(settings: ["theme": "dark", "language": "en", "region": "US"])
13print(config)
14// Config {
15//   language: en
16//   region: US
17//   theme: dark
18// }

Pretty Printing in the Debugger

In Xcode's LLDB debugger, you have additional options. The po (print object) command calls debugDescription or description on an object:

 
(lldb) po user

For more structured output, use p with the --raw flag or dump:

 
(lldb) e dump(user)

You can also set a breakpoint and add a debugger command action to automatically print formatted output without pausing execution.

Common Pitfalls

  • Using JSONSerialization on dictionaries containing non-JSON types (custom objects, Date, URL), which causes silent failure with try?
  • Forgetting that print() on [String: Any] never produces indented output regardless of dictionary size
  • Assuming dictionary key order is consistent across runs without using .sortedKeys
  • Using dump() in production logging, where its reflection-based format is harder to parse programmatically
  • Not handling the optional results from JSON serialization, which hides errors

Summary

  • print() gives single-line output that is only useful for very small dictionaries
  • dump() provides indented, structured output for any Swift type with no setup required
  • JSONSerialization with .prettyPrinted produces clean JSON output for JSON-compatible dictionaries
  • JSONEncoder is the type-safe choice for Codable model objects
  • CustomStringConvertible lets you define reusable formatting for custom types
  • In Xcode, use po and dump() in the LLDB debugger for quick inspection during debugging sessions

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.