Skip to content

Instantly share code, notes, and snippets.

@a-voronov
Created May 2, 2019 16:15
Show Gist options
  • Save a-voronov/9a1236daa5bd0b2e99d1cfcb7daf2dc8 to your computer and use it in GitHub Desktop.
Save a-voronov/9a1236daa5bd0b2e99d1cfcb7daf2dc8 to your computer and use it in GitHub Desktop.
Pretty Swift.Any description
func prettyDescription(of subject: Any, indentCoeff: Int = 0) -> String {
let mirror = Mirror(reflecting: subject)
let properties = mirror.children
guard let displayStyle = mirror.displayStyle else {
return String(reflecting: subject)
}
let indent = "\t"
let globalIndent = String(repeating: indent, count: indentCoeff)
switch displayStyle {
case .tuple:
guard !properties.isEmpty else {
return "()"
}
let descriptions = properties.reduce([String]()) { acc, property in
let label = property.label.map { lbl in lbl.first == "." ? "" : lbl + ": " } ?? ""
return acc + [label + prettyDescription(of: property.value, indentCoeff: indentCoeff + 1)]
}
return aligned(items: descriptions, indent: indent, globalIndent: globalIndent, start: "(", end: ")")
case .collection, .set:
guard !properties.isEmpty else {
return "[]"
}
let descriptions = properties.reduce([String]()) { acc, property in
return acc + [prettyDescription(of: property.value, indentCoeff: indentCoeff + 1)]
}
return aligned(items: descriptions, indent: indent, globalIndent: globalIndent, start: "[", end: "]")
case .dictionary:
guard !properties.isEmpty else {
return "[:]"
}
let descriptions = properties.reduce([String]()) { acc, property in
let pair = Array(Mirror(reflecting: property.value).children)
let key = prettyDescription(of: pair[0].value, indentCoeff: indentCoeff + 1)
let value = prettyDescription(of: pair[1].value, indentCoeff: indentCoeff + 1)
return acc + [key + ": " + value]
}
return aligned(items: descriptions, indent: indent, globalIndent: globalIndent, start: "[", end: "]")
case .enum:
if let subject = subject as? CustomDebugStringConvertible {
return subject.debugDescription
}
guard let property = properties.first else {
return String(reflecting: subject)
}
let associatedValue = prettyDescription(of: property.value, indentCoeff: indentCoeff)
let associatedValueDescription = associatedValue.first == "(" && associatedValue.last == ")"
? associatedValue
: "(\(associatedValue))"
return "\(String(reflecting: mirror.subjectType)).\(property.label ?? "")" + associatedValueDescription
case .struct, .class:
if let subject = subject as? CustomDebugStringConvertible {
return subject.debugDescription
}
guard !properties.isEmpty else {
return String(reflecting: subject)
}
let descriptions = properties.reduce([String]()) { acc, property in
let label = property.label.map { $0 + ": " } ?? ""
return acc + [label + prettyDescription(of: property.value, indentCoeff: indentCoeff + 1)]
}
let description = aligned(items: descriptions, indent: indent, globalIndent: globalIndent, start: "(", end: ")")
return String(reflecting: mirror.subjectType) + description
case .optional:
guard let property = properties.first else {
return String(reflecting: subject)
}
return prettyDescription(of: property.value, indentCoeff: indentCoeff + 1)
}
}
func aligned(items: [String], indent: String, globalIndent: String, start: String, end: String) -> String {
let description = items
.map { globalIndent + indent + $0 }
.joined(separator: ",\n")
return "\(start)\n" + description + "\n" + globalIndent + end
}
@a-voronov
Copy link
Author

Example

Some dummy data - nested types, optionals, full/empty collections, enums with/without associated values, tuples, functions, etc.

struct Me {
    enum En {
        case e
        case f(Int)
        case g(Int, String)
    }
    class Moo {}
    struct Foo {}
}
struct Fizz {
    let buzz: Buzz? = Buzz()
    let fn: () -> String = { return "yo" }
    var foo = "foo"
}
class Fuzz {
    var foo = "foo"
}
struct Buzz {
    let x: Int? = 23
    let fuzz = Fuzz()
}

let example = (
    x: 42,
    xx: (
        y: "yoo",
        z: [
            "a": [1,2,3,4,5],
            "b": Me.En.e,
            "c": (e: 22, f: 53),
            "d": [String:Int](),
            "e": Me.En.f(42),
            "ee": Me.En.g(27, "buzzzz"),
            "pff": Me.Foo()
        ],
        zz: (
            t: "moo",
            u: "foo",
            f: Me.Moo()
        ),
        fizz: Fizz(),
        buzz: Buzz()
    )
)

Prettified result:

print(prettyDescription(of: example))
(
    x: 42,
    xx: (
        y: "yoo",
        z: [
            "b": Me.En.e,
            "e": Me.En.f(42),
            "c": (
                e: 22,
                f: 53
            ),
            "pff": Me.Foo(),
            "d": [:],
            "ee": Me.En.g(
                27,
                "buzzzz"
            ),
            "a": [
                1,
                2,
                3,
                4,
                5
            ]
        ],
        zz: (
            t: "moo",
            u: "foo",
            f: Me.Moo
        ),
        fizz: Fizz(
            buzz: Buzz(
                    x: 23,
                    fuzz: Fuzz(
                        foo: "foo"
                    )
                ),
            fn: (Function),
            foo: "foo"
        ),
        buzz: Buzz(
            x: 23,
            fuzz: Fuzz(
                foo: "foo"
            )
        )
    )
)

Standard reflecting print

print(String(reflecting: example))

(x: 42, xx: (y: "yoo", z: ["b": Me.En.e, "e": Me.En.f(42), "c": (e: 22, f: 53), "pff": Me.Foo(), "d": [:], "ee": Me.En.g(27, "buzzzz"), "a": [1, 2, 3, 4, 5]], zz: (t: "moo", u: "foo", f: Me.Moo), fizz: Fizz(buzz: Optional(Buzz(x: Optional(23), fuzz: Fuzz)), fn: (Function), foo: "foo"), buzz: Buzz(x: Optional(23), fuzz: Fuzz)))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment