Mirror Mirror on the Wall

Swift offers several key technologies that customize the way your constructs represent themselves. These mechanisms include printing, mirroring, and quick looking. This write-up surveys these items and shows how you can use custom protocols to precisely build feedback in your apps and debugging tools.

Printing

I’ve already written extensively about printing and logging. Here’s a quick recap:

Printing: The print() function presents user-facing output. The print function is updated for Swift 2.0 and takes the place of both print/println from Swift 1.x. An extra appendNewline argument prevents carriage returns.

Debug printing: The debugPrint() function is developer-facing. It typically includes formatting (such as extra quote marks or raw values) intended to support debugging tasks. It too has dropped debugPrintln, and uses the same appendNewline parameter.

For example, if you have a structure like this:

struct Segment {
    var p0 = CGPointZero; var p1 = CGPointZero
    init(_ p0 : CGPoint, _ p1 : CGPoint) {
        self.p0 = p0; self.p1 = p1
    }
}

The default print / debugPrint output looks something like this.

Segment(p0: (5.0, 5.0), p1: (2.0, 7.0))

Custom print values: You customize printing by adopting the CustomStringConvertible and CustomDebugStringConvertible protocols. Pre 2.0 these were respectively called Printable and DebugPrintable.  When adopted, these protocols change the way a construct prints itself and participates in strings, for example, “\(s)”.

The implementation requirements remain unchanged, even though the protocol names have updated. You create description and/or debugDescription properties. These are both String types. 

NSLog: The old style NSLog function remains available and works just like it did in Objective-C, but with a minimum of @-signs. Output goes to both the local and system console .

Dumping

Swift supports an additional mechanism for reviewing constructs. Reflection and mirroring enable you to explore an instance’s underlying structure . Mirroring functions and protocols create an alternative to simple logging. They present a structure for inspection.

The dump() function prints to output streams, just like print() and debugPrint(), but creates structure-specific results as you see in the following example. Its contents represent an item’s mirror.

▿ Segment
 ▿ p0: (5.0, 5.0)
 - x: 5.0
 - y: 5.0
 ▿ p1: (2.0, 7.0)
 - x: 2.0
 - y: 7.0

That same mirror appears in playgrounds and is used to create the items you view in result views.

timeline12

QuickLook

Mirroring and QuickLooking work hand-in-hand. A custom QuickLook creates meaningful in-line previews of Swift constructs, enabling you to move beyond simple text-based output.  In this example, QuickLooking each point reveals further detail about the component points:

Screen Shot 2015-06-16 at 8.28.39 AM

Reflection also powers lldb output in standard projects.

timeline10

Building Custom Quick Looks

Swift makes it easy to create custom Quick Look items that express constructs as colors, images, paths, and other high-level representations. I’ve written about this customizability both recently and in the past. In Swift 2, you conform to CustomPlaygroundQuickLookable and build a customPlaygroundQuickLook() function that returns a member of the QuickLookObject enumeration.

Screen Shot 2015-06-16 at 9.23.51 AM

Please note that there’s a currently bug in playgrounds that flips the coordinate system when rendering UIBeziers. Radar already filed.

Building Custom Mirrors

Custom Mirrors were introduced in Swift 2.0. They enable you to create structures that better represent both the actual content of your instances and the semantic meaning of that content in one go. To explore custom mirrors, comment out any custom QuickLook work you may have added as the one tends to overwhelm the other in the playground.

Take the Segment struct for example. In my mind, a segment might establish a baseline for a rotated and translated coordinate system. It makes sense not just to show the two points, but also the angle created from p1 to p0.

Build mirrors by implementing CustomReflectable. Be flexible. Your mirror needn’t adhere to the underlying data structure that powers your instance. You can add any description and information that supports understanding your construct in a way that speaks to you as a developer.

For this example, I decided to combine the two points into a single element (“segment”) and break out the degree information:

extension Segment : CustomReflectable {
    var angleInDegrees : CGFloat {
    let angle = atan2(p1.y - p0.y, p1.x - p0.x)
    return angle * CGFloat(180.0 / M_PI)
 }
 
    func customMirror() -> Mirror {
        return Mirror(self, children: [
            "segment" : "\(p0)...\(p1)",
            "degrees" : 
                String(format:"%0.2f°", angleInDegrees),
        ])
    }
}

Now the segment mirrors like this. It is structurally and conceptually distinct from the strict segment -> point -> coordinate layout presented by the default mirror.

▿ mirrorlldb.Segment
 - segment : "(5.0, 5.0)...(2.0, 7.0)"
 - degrees : "146.31°"

It is not any less useful and provides greater semantic understanding of how the segment struct will be used within my app. By raising the abstraction, I’ve made this reflection more pertinent to my development and debugging.

Recursively Mirrored Structures

There is a point where the built-in custom system doesn’t really get you where you want to go. It’s new technology and it can be a little brittle. Let me give you some examples.

I’ve been doing a lot of work with OptionSetType constructs. These  enable you to work with bit flags as if they were first class sets. Internally, however, they are nothing more than a single raw value.

Screen Shot 2015-06-16 at 9.41.23 AM

The default reflection is nearly useless. Yes, I can use some math and figure out that the flags are 1<<1 and 1<<5 but doing so is a pointless waste of my time. Instead, I want to make reflection work for me.

The simplest way to do this is to implement an extension that creates a dictionary representation for the child flags:

extension Features : CustomReflectable {
    public func customMirror() -> Mirror {
        var dict = [String:Int]()
        for (flag, value) in zip(names, rawflagset) {
            dict[flag] = value
        }
        let set = "[" + (", ".join(names)) + "]"
        return Mirror(self, children: [
            "rawValue": rawValue,
            "members": set,
            "flags": dict
        ])
    }
}

The results are much improved. Each flag is broken out and its raw value displayed next to a meaningful name.

Screen Shot 2015-06-16 at 9.45.12 AM

You run into the first of several bugs when you try to create an array of feature packages. The representation system simply breaks down.  You cannot get past those ellipses, especially for the child flags.

Screen Shot 2015-06-16 at 9.48.11 AM

Going Whole Hog Flat Out Mirror Crazy

The second problem occurs when you attempt to represent entries in the children dictionary as Features instances. The basic dictionary approach just isn’t made for multi-level finesse or recursive descriptions.

Decomposed, the option set children are option sets themselves, albeit ones with a single option each. The basic CustomReflectable protocol isn’t set up for recursive mirroring at this time.

I did manage to get a prototype going, which you see the code for this at the bottom of the post. I cannot see this workaround being a long-term solution for beyond-the-basics mirroring.

To build my recursively mirrored system, I ended up creating custom reflection rendering. This allowed me to perform a custom dump of my mirror type, which could not otherwise be handled by lldb or the playground’s in-system visualizer.

What follows are two dumps and my heinous custom reflection system. The first custom dump represents the Features option set without custom reflection. What you get is simply a raw value with the value of 34.

My reflection is enabled in the second custom dump. Here, the option set gains real children, not just dictionary entries.  The alarm system and leather interior flags become actual child elements, each with its own raw value and full reflection.

To make everything work, my final listing adopts both MirrorType and CustomReflectable. Only then can the struct provide access to a child count and indexed substructures and extend custom mirroring to the set decomposition.

Comments are closed.