Mirror / Custom​Reflectable / Custom​Leaf​Reflectable

Reflection in Swift is a limited affair, providing read-only access to information about objects. In fact, this functionality might be better described as introspection rather than reflection.

But can you really blame them for going with that terminology?

“Introspection” is such a bloviated and acoustically unpleasant word compared to “Reflection.” And how could anyone pass on the nominal slam dunk of calling the type responsible for reflecting an object’s value a Mirror? What equivalent analogy can we impart for the act of introspection? struct LongDarkTeaTimeOfTheSoul? (As if we didn’t have better things to do than ask our Swift code about the meaning of Self)

Without further ado, let’s take a peek inside the inner workings of how Swift exposes that of our own code: Mirror, CustomReflectable, and CustomLeafReflectable.


Swift provides a default reflection, or structured view, for every object. This view is used by Playgrounds (unless a custom description is provided), and also acts as a fallback when describing objects that don’t conform to the CustomStringConvertible or CustomDebugStringConvertible protocols.

You can customize how instances of a type are reflected by adopting the CustomReflectable protocol and implementing the required customMirror property.

The computed customMirror property returns a Mirror object, which is typically constructed by reflecting self and passing the properties you wish to expose to the reflected interface. Properties may be either keyed and passed in a dictionary or unkeyed and passed in an array. You can optionally specify a display style to override the default for the kind of type (struct, class, tuple, etc.); if the type is a class, you also have the ability to adjust how ancestors are represented.

Conforming to CustomReflectable

To get a better understanding of how this looks in practice, let’s look at an example involving an implementation of a data model for the game of chess:

A chessboard comprises 64 squares, divided into 8 rows (called ranks) and 8 columns (called files). A compact way to represent each location on a board is the 0x88 method, in which the rank and file are encoded into 3 bits of each nibble of a byte (0b0rrr0fff).

For simplicity, Rank and File are typealias’d to UInt8 and use one-based indices; a more complete implementation might use enumerations instead.

struct ChessBoard {
    typealias Rank = UInt8
    typealias File = UInt8

    struct Coordinate: Equatable, Hashable {
        private let value: UInt8

        var rank: Rank {
            return (value >> 3) + 1
        }

        var file: File {
            return (value & 0b00000111) + 1
        }

        init(rank: Rank, file: File) {
            precondition(1...8 ~= rank && 1...8 ~= file)
            self.value = ((rank - 1) << 3) + (file - 1)
        }
    }
}

If we were to construct a coordinate for b8 (rank 8, file “b” or 2), its default reflection wouldn’t be particularly helpful:

let b8 = ChessBoard.Coordinate(rank: 8, file: 2)
String(reflecting: b8) // ChessBoard.Coordinate(value: 57)

57 in decimal is equal to 0111001 in binary, or a value of 7 for rank and 1 for file, which adding the index offset of 1 brings us to rank 8 and file 2.

The default mirror provided for a structure includes each of an object’s stored properties. However, consumers of this API shouldn’t need to know or even care about the implementation details of how this information is stored. It’s more important to reflect the programmatic surface area of the type, and we can use CustomReflectable to do just that:

extension ChessBoard.Coordinate: CustomReflectable {
    var customMirror: Mirror {
        return Mirror(self,
                      children: ["rank": rank, "file": file])
    }
}

Rather than exposing the private stored value property, our custom mirror provides the computed rank and file properties. The result: a much more useful representation that reflects our understanding of the type:

String(reflecting: b8) // ChessBoard.Coordinate(rank: 8, file: 2)

Introspecting the Custom Mirror

The String(reflecting:) initializer is one way that mirrors are used. But you can also get at them directly through the customMirror property to access the type metatype and display style, and enumerate each of the mirror’s children.

b8.customMirror.subjectType // ChessBoard.Coordinate.Type
b8.customMirror.displayStyle // struct

for child in b8.customMirror.children {
    print("\(child.label ?? ""): \(child.value)")
}

Customizing the Display of a Custom Mirror

There are differently-shaped mirrors for each kind of Swift value, each with their own particular characteristics.

For example, the most important information about a class is its identity, so its reflection contains only its fully-qualified type name. Whereas a structure is all about substance and offers up its type name and values.

If you find this to be less than flattering for your special type you can change up your look by specifying an enumeration value to the displayStyle attribute in the Mirror initializer.

Here’s a sample of what you can expect for each of the available display styles when providing both labeled and unlabeled children:

Labeled Children

extension ChessBoard.Coordinate: CustomReflectable {
    var customMirror: Mirror {
        return Mirror(self,
                      children: ["rank": rank, "file": file])
                      displayStyle: displayStyle)
    }
}
Style Output
class ChessBoard.Coordinate
struct Coordinate(rank: 8, file: 2)
enum Coordinate(8)
optional 8
tuple (rank: 8, file: 2)
collection [8, 2]
set {8, 2}
dictionary [rank: 8, file: 2]

Unlabeled Children

extension ChessBoard.Coordinate: CustomReflectable {
    var customMirror: Mirror {
        return Mirror(self,
                      unlabeledChildren: [rank, file],
                      displayStyle: displayStyle)
    }
}
Style Output
class ChessBoard.Coordinate
struct Coordinate()
enum Coordinate(8)
optional 8
tuple (8, 2)
collection [8, 2]
set {8, 2}
dictionary [ 8, 2]

Reflection and Class Hierarchies

Continuing with our chess example, let’s introduce some class by defining an abstract Piece class and subclasses for pawns, knights, bishops, rooks, queens, and kings. (For flavor, let’s add standard valuations while we’re at it).

class Piece {
    enum Color: String {
        case black, white
    }

    let color: Color
    let location: ChessBoard.Coordinate?

    init(color: Color, location: ChessBoard.Coordinate? = nil) {
        self.color = color
        self.location = location
        precondition(type(of: self) != Piece.self, "Piece is abstract")
    }
}

class Pawn:   Piece { static let value = 1 }
class Knight: Piece { static let value = 3 }
class Bishop: Piece { static let value = 3 }
class Rook:   Piece { static let value = 5 }
class Queen:  Piece { static let value = 9 }
class King:   Piece {}

So far, so good. Now let’s use the same approach as before to define a custom mirror (setting the displayStyle to struct so that we get more reflected information than we would otherwise for a class):

extension Piece: CustomReflectable {
    var customMirror: Mirror {
        return Mirror(self,
                      children: ["color": color,
                                 "location": location ?? "N/A"],
                      displayStyle: .struct))
    }
}

Let’s see what happens if we try to reflect a new Knight object:

let knight = Knight(color: .black, location: b8)
String(reflecting: knight) //
Piece(color: black, location: (rank: 8, file: 2))

Hmm… that’s no good. What if we try to override that in an extension to Knight?

extension Knight {
    override var customMirror: Mirror {
        return Mirror(self,
                      children: ["color": color,
                                 "location": location ?? "N/A"])
    }
} // ! error: overriding declarations in extensions is not supported

No dice (to mix metaphors).

Let’s look at three different options to get this working with classes.

Option 1: Conform to CustomLeafReflectable

Swift actually provides something for just such a situation — a protocol that inherits from CustomReflectable called CustomLeafReflectable.

If a class conforms to CustomLeafReflectable, its reflections of subclasses will be suppressed unless they explicitly override customMirror and provide their own Mirror.

Let’s take another pass at our example from before, this time adopting CustomLeafReflectable in our initial declaration:

class Piece: CustomLeafReflectable {
    

    var customMirror: Mirror {
        return Mirror(reflecting: self)
    }
}

class Knight: Piece {
    

    override var customMirror: Mirror {
        return Mirror(self,
                      children: ["color": color,
                                 "location": location ?? "N/A",
                                 "value": Knight.value],
                      displayStyle: .struct)
    }
}

Now when go to reflect our knight, we get the correct type — Knight instead of Piece.

String(reflecting: knight)
// Knight(color:Piece.Color.black, location: (rank: 8, file: 2), value: 3)

Unfortunately, this approach requires us to do the same for each of the subclasses. Before we suck it up and copy-paste our way to victory, let’s consider our alternatives.

Option 2: Encode Type in Superclass Mirror

A simpler alternative to going the CustomLeafReflectable route is to include the type information as a child of the mirror declared in the base class.

extension Piece: CustomReflectable {
    var customMirror: Mirror {
        return Mirror(self,
                      children: ["type": type(of: self),
                                 "color": color,
                                 "location": location ?? "N/A"],
                      displayStyle: .struct)
    }
}

String(reflecting: knight)
// Piece(type: Knight, color: black, location: (rank: 8, file: 2))

This approach is appealing because it allows reflection to be hidden as an implementation detail. However, it can only work if subclasses are differentiated by behavior. If a subclass adds stored members or significantly differs in other ways, it’ll need a specialized representation.

Which begs the question: why are we using classes in the first place?

Option 3: Avoid Classes Altogether

Truth be told, we only really introduced classes here as a contrived example of how to use CustomLeafReflectable. And what we came up with is kind of a mess, right? Ad hoc additions of value type members for only some of the pieces (kings don’t have a value in chess because they’re a win condition)? An “abstract” base class that crashes if you try to instantiate it (for current lack of a first-class language feature)? Yikes. These are the kinds of things that give OOP a bad name.

We can mitigate nearly all of these problems by adopting a protocol-oriented approach like the following:

First, define a protocol for Piece and a new protocol to encapsulate pieces that have value, called Valuable:

protocol Piece {
    var color: Color { get }
    var location: ChessBoard.Coordinate? { get }
}

protocol Valuable: Piece {
    static var value: Int { get }
}

Next, define value types — an enum for Color and structures for each piece:

enum Color: String {
    case black, white
}



struct Knight: Piece, Valuable {
    static let value: Int = 3
    let color: Color
    let location: ChessBoard.Coordinate?
}


Although we can’t provide conformance by protocol through an extension, we can provide a default implementation for the requirements and declare adoption in extensions to each concrete Piece type. As a bonus, Valuable can provide a specialized variant that includes its value:

extension Piece {
    var customMirror: Mirror {
        return Mirror(self,
                      children: ["color": color,
                                 "location": location ?? "N/A"],
                      displayStyle: .struct)
    }
}

extension Valuable{
    var customMirror: Mirror {
        return Mirror(self,
                      children: ["color": color,
                                 "location": location ?? "N/A",
                                 "value": Self.value],
                      displayStyle: .struct)
    }
}


extension Knight: CustomReflectable {}

Putting that all together, we get exactly the behavior we want with the advantage of value semantics.

let b8 = ChessBoard.Coordinate(rank: 8, file: 2)
let knight = Knight(color: .black, location: b8)

String(reflecting: knight)
// Knight(color: black, location: (rank: 8, file: 2))

Ultimately, CustomLeafReflectable is provided mostly for compatibility with the classical, hierarchical types in Objective-C frameworks like UIKit. If you’re writing new code, you need not follow in their footsteps.


When Swift first came out, longtime Objective-C developers missed the dynamism provided by that runtime. And — truth be told — truth be told, things weren’t all super great. Programming in Swift could, at times, feel unnecessarily finicky and constraining. “If only we could swizzle…” we’d grumble.

And perhaps this is still the case for some of us some of the time, but with Swift 4, many of the most frustrating use cases have been solved.

Codable is a better solution to JSON serialization than was ever available using dynamic introspection. Codable can also provide mechanisms for fuzzing and fixtures to sufficiently enterprising and clever developers. The CaseIterable protocol fills another hole, allowing us a first-class mechanism for getting an inventory of enumeration cases. In many ways, Swift has leap-frogged the promise of dynamic programming, making APIs like Mirror largely unnecessary beyond logging and debugging.

That said, should you find the need for introspection, you’ll know just where to look.

NSMutableHipster

Questions? Corrections? Issues and pull requests are always welcome.

This article uses Swift version 4.2 and was last reviewed on November 14, 2018. Find status information for all articles on the status page.

Written by Mattt
Mattt

Mattt (@mattt) is a writer and developer in Portland, Oregon.

Next Article

Our third annual WWDC NSHipster Pub Quiz! Scores of developers, dozens of teams, and a down-to-the-wire finish. How will you fare?