iOS
Cool Swift Tricks 1: Mirror, Mirror
Matt Neuburg
Written on August 21, 2020

This is the first of four posts about random little Swift tips and tricks for you to amaze your friends and confound your enemies. All of these came up in my real code recently, and they really did surprise one or more of my co-workers. The Swift language has a lot of cool features hidden away in its nooks and crannies, but most real developers are too busy getting real work done to keep up with all of them; so every once in a while I’ve submitted a pull request that made someone say, “Hey, I didn’t know about that!” If you already knew about these features, you can feel smug, and if you didn’t, now you do, so you can feel smug anyway.
We had a bunch of value types in our app, basically just collections of properties. While debugging, these would all print out nicely in the LLDB console as a list of their properties along with their values — except for one. After a while, I realized what the problem was: the ones that printed out nicely were structs, but the problematic one was a class.
For example:
struct Person {
let name: String
let age: Int
let pet: Pet
}
class Pet {
let name: String
let license: Int
init(name:String, license:Int) {
self.name = name; self.license = license
}
}
let pet = Pet(name: "Speedy", license: 123)
let person = Person(name: "Lear", age: 80, pet: pet)
Now in LLDB we try to examine person
, and here’s what we get:
(lldb) po person
▿ Person
- name : "Lear"
- age : 80
▿ pet : <Pet: 0x600001a22040>
So LLDB is happy to give me a property-based picture of a Person, but not of a Pet. I have no idea why that is. Now, you could say, “Call dump
“; but there’s no time for that, because I have lots of these Pet objects popping up all the time. You could also say, “Write a description
property for Pet”; but there’s no time for that either, because in real life this class has a lot of properties — and besides, if we add or remove a property, I have to remember to rewrite my description
implementation.
What I do have time for is to whip up a protocol that uses Mirror introspection to report on a type’s properties in general, without my having to specify what they are:
protocol Introspectable: CustomDebugStringConvertible {}
extension Introspectable {
var debugDescription: String {
var output = ""
for child in Mirror(reflecting: self).children {
output += (child.label ?? "") + ": " + String(describing: child.value) + "n"
}
return output
}
}
Now all I have to do is declare that Pet conforms to Introspectable, thereby injecting my definition of debugDescription
into it. The po
command in LLDB uses debugDescription
if it exists, so now here’s what happens in LLDB:
(lldb) po person
▿ Person
- name : "Lear"
- age : 80
▿ pet : name: Speedy
license: 123
It’s not elegant, and the formatting isn’t perfect, but at least I see the Pet properties and their values so I can get on with debugging the app. Even better, it surprised a colleague, who didn’t know that Mirror could be put to such a practical use.
Check out part 2 of Cool Swift Tricks: No Escape