Reflection allows applications to inspect, analyze, and modify their structure and behavior at runtime. With reflection, we have the ability to discover information about types at runtime. We can think of reflection as a way for a program to look at and inspect itself.
Reflection in Swift is implemented using the Mirror API. This API enables us to inspect instances of classes, structs, and enumerations, offering valuable insights into the instance’s type and the values of its properties. This capability proves especially beneficial when handling dynamic or unknown types, as it enables querying of an instance without prior knowledge of its exact type.
Through the Mirror API, we can access the instance’s properties, termed as children, providing a means to examine the stored values within an instance. This is particularly useful when dealing with complex data structures or when seeking to comprehend the internal mechanisms of a specific type. Let’s explore what the Mirror API can do.
Before deciding to use Swift’s reflection capabilities, we must take into account the performance implications. Reflective tasks typically require more computing power than direct access methods. This increased overhead means that using reflection extensively, in parts of the application that are critical for performance, is not recommended.
The Mirror API gives us access to information about Swift objects at runtime. Let’s take a look at this.
Getting Started with the Mirror API
Before we explore the capabilities of the Mirror API, we will need to create a type that can be used for the examples. Let’s create the following Person structure:
struct Person {
var firstName: String
var lastName: String
var age: Int
}
In this structure, we define three properties, firstName and lastName, both of which are strings, and age, which is an integer. Now, let’s see how we would use the Mirror API with our Person type:
let person = Person(firstName: "Jon", lastName: "Hoffman", age: 55)
let mirror = Mirror(reflecting: person)
This code starts off by creating an instance of the Person type. We then create an instance of the Mirror type using the Person instance. We can now use the mirror instance to access different attributes of the person instance.
Let’s look at the displayStyle and subjectType values of our person instance using the mirror instance. The following code will display them:
print("Display Style: \(mirror.displayStyle!)")
print("Subject Type: \(mirror.subjectType)")
displayStyle is an enumeration that contains the following cases:
case class
case collection
case dictionary
case enum
case optional
case set
case struct
case tuple
From this list, we can see that the displayStyle tells us the underlying type of the instance.
subjectType is the name of the type.
If we ran this code, we would see the following output:
Display Style: Optional(Swift.Mirror.DisplayStyle.struct)
Subject Type: Person
The Mirror API could also be used to inspect the properties of the instance. Let’s see how we could do this:
for (label, value) in mirror.children {
print("Property: \(label ?? "Unknown"), Value: \(value)")
}
The children property contains a list of the instance’s properties and their values. It’s important to note that this property only contains the stored properties of the instance and other elements, like computed properties, are not part of this collection. The output of this code would look something like this:
Property: firstName, Value: Jon
Property: lastName, Value: Hoffman
Property: age, Value: 55
Thank you for reading this post, I hope it helped you learn more about the reflection and the Swift language. You can read more about reflection and the Swift language in my book Mastering Swift 6.