Last active
December 20, 2017 17:25
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 // ! | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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