Reflection in Swift

Or are we talking introspection?

Image for post
Image for post
Does this represent Reflection, or Introspection??

It is often claimed that reflection is not really used in Swift as it is a statically typed language, but actually it gives us read-only access to an object’s properties (importantly) at runtime.

If you have a class you can use reflection to iterate through all of the values

Difficulty: Easy | Normal | Challenging

Prerequisites:

  • Swift data types, some understanding of OO concepts.

Terminology

Introspection: A way to look at an object’s properties without modifying them. Swift reflection should rather be called introspection.

Mirror: A description of the parts that make up a particular instance. For Swift, access to these objects is read-only.

Reflection: An API used to examine or modify the behaviour of methods, classes and interfaces at runtime.

Uses of mirror in Swift

Inspect type data of structs or classes

enum Species {
case dog
case cat
}
struct Animal {
var name: String
var species: Species
}
var taylor = Animal(name: "Derek", species: .dog)
var mirror = Mirror(reflecting: taylor)
print (mirror) // "Mirror for Animal"

The printing here that prints to the console “Mirror for Animal” is not too interesting. The result that you should be looking for is to iterate through the mirror to print out the properties of the Animal struct.

for case let (label?, value) in mirror.children {
print (label, value) // "name Derek", "species dog"
}

Iterate over tuples inspecting elements using Mirror

// Tuples can take different Types of datalet tuple = (1, 2, "3")
let tupleMirror = Mirror(reflecting: tuple)
let tupleElements = tupleMirror.children.map({ $0.value })
tupleElements // [1, 2, "3"]

tupleElements here has the type [Any] so we can actually print out those elements that appear to have different types. Fantastic!

Inspect type data of structs or classes

let trevor = Animal(name: "trevor", species: .dog(breed: "Shetland"))
let trevMirror = Mirror(reflecting: trevor)
for case let (label?, value) in trevMirror.children {
print (label, value) // "name trevor", "species dog(breed: "Shetland")"
}

So we are printing to the console two elements:

“name trevor”

“species dog(breed: “Shetland”)”

Runtime analysis of object conformance

let trevor = Animal(name: “trevor”, species: .dog(breed: “Shetland”))
for property in Mirror(reflecting: trevor).children {
print("name: \(String(describing: property.label)) type: \(type(of: property.value))")
}

So we are printing to the console two elements:

name: Optional(“name”) type: String

name: Optional(“species”) type: Species

Testing

let aMirror = Mirror(reflecting: trevor)
aMirror.children.count

Debugging with CustomReflectable

An example of this is debugging self

po self

this can give a less than helpful description

<MemoryTest.ViewController: 0x7ffe1f607010>

so to give this a little more information we can write an extension for the viewcontroller:

extension UIViewController: CustomReflectable{public var customMirror: Mirror {let children = KeyValuePairs<String, Any>(dictionaryLiteral:("title", title!))return Mirror(NSUserActivity.self, children: children, displayStyle: .class, ancestorRepresentation: .suppressed)}}

which of course relies upon you having set a title in your view controller instance!

This then returns the (very marginally) more useful po

(lldb) po self▿ <MemoryTest.ViewController: 0x7fdc5cc06690>- title : "mytitle"

Converting structs to Core Data

Conclusions

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store