What is the difference between print and debugPrint in Swift?

Explain that `print` prefers a user-facing string while `debugPrint` prefers a debug representation, then show when their output is identical and when it diverges

Answer

Core answer

The key difference is this: print uses String(describing:) and debugPrint uses String(reflecting:).

In practice, print aims for a more readable, user-facing representation, while debugPrint asks for a representation that is more useful during debugging. But that does not mean debugPrint is always different or always includes type information. For many values, both functions produce the same output.

1. Sometimes they are exactly the same

For many standard values, there is no visible difference at all:

swift
let count = 42
let ready = true

print(count)
debugPrint(count)

print(ready)
debugPrint(ready)

Run compiles and executes on the server; output shows below.

For values like these, both representations are simple and unambiguous, so the output is the same.

2. Strings are a good example of when they differ

Strings often show the difference clearly. print writes the string as a user would normally read it. debugPrint shows the debug form, which keeps quotes and escape sequences visible.

swift
let message = "Hello\nSwift"

print(message)
debugPrint(message)

Run compiles and executes on the server; output shows below.

Here, print renders the newline as an actual line break. debugPrint shows the string more literally, which is useful when you want to see hidden characters and escaping behavior.

3. How CustomStringConvertible changes print

If a type conforms to CustomStringConvertible, it provides a custom description. That is the representation print prefers.

If the type does not also provide a separate debug representation, debugPrint falls back to that same description.

swift
struct User: CustomStringConvertible {
    let id: Int

    var description: String {
        "User #\(id)"
    }
}

let user = User(id: 7)

print(user)
debugPrint(user)

Run compiles and executes on the server; output shows below.

This is an important interview nuance: if a type only customizes description, print and debugPrint can still look identical.

4. How CustomDebugStringConvertible changes debugPrint

If a type also conforms to CustomDebugStringConvertible, it can provide a separate debugDescription. That is what debugPrint prefers.

There is an important nuance here: the fallback works in the other direction too. If a type only provides debugDescription, print can end up showing that text as well. The real difference is preference order, not a hard wall where each function can only ever use one protocol.

swift
struct User: CustomStringConvertible, CustomDebugStringConvertible {
    let id: Int
    let email: String

    var description: String {
        "User #\(id)"
    }

    var debugDescription: String {
        "User(id: \(id), email: \(email))"
    }
}

let user = User(id: 7, email: "ana@example.com")

print(user)
debugPrint(user)

Run compiles and executes on the server; output shows below.

Now the difference is explicit:

  1. print(user) uses description, which stays concise.
  2. debugPrint(user) uses debugDescription, which can expose more implementation detail that helps during development.

Interview angle

Walk the answer in this order: print uses String(describing:) -> debugPrint uses String(reflecting:) -> many values print the same way -> strings often show the difference because debug output preserves escapes and quotes -> CustomStringConvertible controls description -> CustomDebugStringConvertible controls debugDescription when you want a separate debugging view.