Skip to content

Instantly share code, notes, and snippets.

@igor9silva
Last active December 20, 2017 17:25
Show Gist options
  • Save igor9silva/b62c347db3d97d39612fa24c1b1a4e28 to your computer and use it in GitHub Desktop.
Save igor9silva/b62c347db3d97d39612fa24c1b1a4e28 to your computer and use it in GitHub Desktop.
A swift helper function to log data structures. Supports structs, classes, arrays, dictionaries, functions, tuples, enums.
import Foundation
public func classDescription(_ obj: Any,
shouldIncludeType: Bool = false,
indentLevel: Int = 0) -> String {
return classDescription(obj,
shouldIncludeType: shouldIncludeType,
indentLevel: indentLevel,
optionalType: .non)
}
fileprivate func classDescription(_ obj: Any,
shouldIncludeType: Bool = false,
indentLevel: Int = 0,
optionalType: OptionalType) -> String {
// Create a 'Mirror' based on the object
let mirror = obj as? Mirror ?? Mirror(reflecting: obj)
// If it's a primitive value, return itself
if mirror.children.count == 0 {
if shouldIncludeType {
var optionalChar = ""
switch optionalType {
case .non: optionalChar = ""
case .optional: optionalChar = "?"
case .implicitlyUnwrapped: optionalChar = "!"
}
return "\(mirror.subjectType)\(optionalChar)(\(obj))" // Int(10)
} else {
return "\(obj)" // 10
}
}
// Check if it's an optional (? or !)
if mirror.children.count == 1 {
if mirror.displayStyle == .optional {
return classDescription(mirror.children.first!.value,
shouldIncludeType: shouldIncludeType,
indentLevel: indentLevel,
optionalType: .optional)
}
// For some reason, ImplicitlyUnwrappedOptional have displayStyle == .enum
else if mirror.displayStyle == .enum {
return classDescription(mirror.children.first!.value,
shouldIncludeType: shouldIncludeType,
indentLevel: indentLevel,
optionalType: .implicitlyUnwrapped)
}
}
var str = ""
if shouldIncludeType {
str += "\(mirror.subjectType) {" // Person { }
} else {
str += "{" // { }
}
var first = true
var children = mirror.children.map { ($0.label, $0.value) } // Map as a tuple array
if let superclass = mirror.superclassMirror {
children.insert(("superclass", superclass), at: 0) // If it has a superclass, add as a child labeled 'superclass'
}
for (label, value) in children {
if first {
first = false
} else {
str += ", "
}
// Recursively adds value's classDescription, increasing identLevel by 1
str += "\n\(String(repeating: "\t", count: indentLevel + 1))"
if let label = label, label.count > 0 {
str += "\(label): "
}
str += "\(classDescription(value, shouldIncludeType: shouldIncludeType, indentLevel: indentLevel + 1))"
}
str += "\n\(String(repeating: "\t", count: indentLevel))}"
return str
}
fileprivate enum OptionalType {
case non
case optional // ?
case implicitlyUnwrapped // !
}
struct Person {
var name: String!
var age: Int!
var house: House!
}
struct House {
var size: Double!
var address: Address!
}
struct Address {
var street: String!
var number: Int?
}
var address = Address()
address.street = "Rua Augusto Paulino"
address.number = 117
var house = House()
house.size = 64
house.address = address
var me = Person()
me.name = "Igor"
me.age = 20
me.house = house
print(classDescription(me))
/*
{
name: Igor,
age: 20,
house: {
size: 64.0,
address: {
street: Rua Augusto Paulino,
number: 117
}
}
}
*/
print(classDescription(me, shouldIncludeType: true))
/*
Person {
name: String!(Igor),
age: Int!(20),
house: House {
size: Double!(64.0),
address: Address {
street: String!(Rua Augusto Paulino),
number: Int?(117)
}
}
}
*/
enum Enum {
case one
case two
}
enum StringEnum: String {
case one
case two
}
enum IntEnum: Int {
case one
case two
}
class Animal {
var name: String
init(name: String) {
self.name = name
}
}
class Dog: Animal {
var owner: String
var function: (String) -> Void
var tuple: (String, Int)
var labeledTuple: (key: String, value: Int)
var array: [Int]
var dictionary: [String: Int]
var enumeration: Enum
var stringEnumeration: StringEnum
var intEnumeration: IntEnum
init(name: String, owner: String) {
self.owner = owner
self.function = { text in print(text) }
self.tuple = ("test", 10)
self.labeledTuple = ("test", 10)
self.array = [10, 20, 30]
self.dictionary = ["ten": 10, "twenty": 20]
self.enumeration = .two
self.stringEnumeration = .one
self.intEnumeration = .two
super.init(name: name)
}
}
print(classDescription(Dog(name: "Rex", owner: "Igor")))
/*
{
superclass: {
name: Rex
},
owner: Igor,
function: (Function),
tuple: {
.0: test,
.1: 10
},
labeledTuple: {
key: test,
value: 10
},
array: {
10,
20,
30
},
dictionary: {
{
key: twenty,
value: 20
},
{
key: ten,
value: 10
}
},
enumeration: two,
stringEnumeration: one,
intEnumeration: two
}
*/
print(classDescription(Dog(name: "Rex", owner: "Igor"), shouldIncludeType: true))
/*
Dog {
superclass: Animal {
name: String(Rex)
},
owner: String(Igor),
function: (String) -> ()((Function)),
tuple: (String, Int) {
.0: String(test),
.1: Int(10)
},
labeledTuple: (key: String, value: Int) {
key: String(test),
value: Int(10)
},
array: Array<Int> {
Int(10),
Int(20),
Int(30)
},
dictionary: Dictionary<String, Int> {
(key: String, value: Int) {
key: String(twenty),
value: Int(20)
},
(key: String, value: Int) {
key: String(ten),
value: Int(10)
}
},
enumeration: Enum(two),
stringEnumeration: StringEnum(one),
intEnumeration: IntEnum(two)
}
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment