Reflection and Introspection in Swift: Peering into the Soul of Code
\n\nHave you ever wondered how Swift "sees" its own structures and classes? How can you find out an object's type, its properties, or even a method signature on the fly without knowing anything about it beforehand? Welcome to the world of reflection and introspection — two powerful mechanisms that turn statically typed Swift into a flexible tool for metaprogramming.
\n\nToday, we won't just cover the theory. We'll write code that examines itself, learn to dynamically check protocols, and even serialize objects without Codable. Let's go!
\n\nWhat are Reflection and Introspection? Don't Confuse These Concepts
\n\nLet's start with clear terminology to avoid getting lost in the depths of the Swift runtime.
\n\n- \n
- Introspection is the ability of a program to "look at itself" during execution: find out an object's type, the number of properties, method names. In Swift, this is implemented through the
Mirrorprotocol and theis,as?,type(of:)operators. \n - Reflection is a broader concept. It's not just observation, but also the ability to influence the program's structure on the fly: call methods by name, change properties, create new types. In Swift, reflection is limited (unlike in Python or Ruby), but its capabilities are impressive. \n
The main difference: introspection is reading, reflection is reading + writing. Swift positions itself as a safe language, so full reflection (as in Objective-C) is not available for pure Swift types. However, for classes inherited from NSObject, more possibilities open up.
Introspection Tools in Swift: From Simple to Complex
\n\n1. The is and as? Operators — Classic Genre
\n\nThe simplest way to find out an object's type is to use a check with is:
let value: Any = "Hello, Swift!"\n\nif value is String {\n print("This is a string!")\n}\n\nif let number = value as? Int {\n print("This is a number: \\(number)")\n} else {\n print("This is not a number")\n}\n\nHere we use is to check the type and as? for safe casting. This is the foundation of any introspection in Swift — you constantly ask the object: "Who are you?".
2. type(of:) — Finding Out the Type Dynamically
\n\nThe type(of:) function returns the metatype (Any.Type) of the passed value. This is useful when you need to get the exact dynamic type, not the declared static one:
class Animal { }\nclass Dog: Animal { }\n\nlet pet: Animal = Dog()\nprint(type(of: pet)) // Dog\n\n// Comparing types\nif type(of: pet) == Dog.self {\n print("This is definitely a dog!")\n}\n\nNote: type(of:) returns the dynamic type, not the static one (Animal). This is a key difference from simple Self.
3. Mirror — X-Ray Vision for Objects
\n\nAnd now for the most interesting part. Mirror is a structure that allows you to look inside any object and get a list of its properties, their names, and values. A true tool for reflection in Swift.
Basic example:
\n\nstruct User {\n let name: String\n var age: Int\n private var secretKey: String\n}\n\nlet user = User(name: "Alexey", age: 30, secretKey: "abc123")\nlet mirror = Mirror(reflecting: user)\n\nprint("Object type: \\(mirror.subjectType)")\nprint("Number of properties: \\(mirror.children.count)")\n\nfor child in mirror.children {\n if let label = child.label {\n print("\\(label): \\(child.value)")\n }\n}\n\nOutput:
\nObject type: User\nNumber of properties: 3\nname: Alexey\nage: 30\nsecretKey: abc123\n\nNote: Mirror can even see private properties! This is powerful, but also dangerous — don't st