Swift
programming languages
reflection
Swift features
software development

Does Swift support reflection?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Swift supports reflection, but in a more limited and intentional form than highly dynamic languages. The primary API is Mirror, which lets you inspect type and property metadata at runtime. Swift favors safety and performance, so reflection is mostly read-oriented and not a replacement for compile-time design.

What Mirror Can Do

Mirror allows you to inspect an instance and iterate over its stored properties.

swift
1import Foundation
2
3struct UserProfile {
4    let id: Int
5    let name: String
6    let isPremium: Bool
7}
8
9let profile = UserProfile(id: 7, name: "Mina", isPremium: true)
10let mirror = Mirror(reflecting: profile)
11
12for child in mirror.children {
13    let label = child.label ?? "unknown"
14    print("\(label): \(child.value)")
15}

This is useful for debugging output, diagnostic tools, and generic logging helpers.

What Swift Reflection Does Not Provide

Swift reflection is not a full runtime metaprogramming system.

  • You cannot add properties or methods dynamically at runtime.
  • You cannot invoke arbitrary methods by name in a fully dynamic style.
  • Mutation through reflection is very limited and typically indirect.

That design keeps Swift predictable and allows stronger compiler optimizations.

Reflection with Classes and Inheritance

Mirror includes superclass information for class instances, which helps when debugging inheritance chains.

swift
1class Animal {
2    let kind: String
3    init(kind: String) { self.kind = kind }
4}
5
6class Dog: Animal {
7    let name: String
8    init(name: String) {
9        self.name = name
10        super.init(kind: "dog")
11    }
12}
13
14let dog = Dog(name: "Rex")
15var current: Mirror? = Mirror(reflecting: dog)
16while let m = current {
17    print("Type:", m.subjectType)
18    for child in m.children {
19        print("-", child.label ?? "unknown", child.value)
20    }
21    current = m.superclassMirror
22}

This can simplify introspection in framework code and debugging tools.

Reflection Versus Protocol-Oriented Design

Many problems solved with reflection in other languages are better solved in Swift with protocols and generics.

swift
1protocol DebugSummary {
2    var debugSummary: String { get }
3}
4
5struct Order: DebugSummary {
6    let id: String
7    let amount: Double
8
9    var debugSummary: String {
10        "Order(id: \(id), amount: \(amount))"
11    }
12}

This gives explicit, compile-time-safe behavior with better performance and clearer intent.

Objective-C Runtime Interop Cases

If you inherit from NSObject, you can use Objective-C runtime features like key-value coding in mixed codebases. That can feel like reflection, but it applies only to compatible types and comes with weaker type safety.

swift
1import Foundation
2
3class Person: NSObject {
4    @objc dynamic var name: String = "Nora"
5}
6
7let person = Person()
8person.setValue("Ivy", forKey: "name")
9print(person.value(forKey: "name") ?? "nil")

Use this when interoperability is required, not as default Swift architecture.

Practical Use Cases

Reflection is useful when you need generic diagnostics and tooling.

  • Structured debug logging.
  • Generic snapshot comparison in tests.
  • Lightweight serialization helpers for internal tools.
  • Developer-facing UI inspectors.

For production business logic, explicit modeling with protocols and typed APIs is usually safer.

Customize Reflection Output

Swift lets you tailor reflected output by conforming to CustomReflectable. This is helpful when default mirrors expose too much internal detail.

swift
1struct Credentials: CustomReflectable {
2    let username: String
3    let token: String
4
5    var customMirror: Mirror {
6        Mirror(self, children: [
7            "username": username,
8            "token": "redacted"
9        ])
10    }
11}
12
13let creds = Credentials(username: "ops", token: "secret")
14print(Mirror(reflecting: creds).children.map { "\($0.label ?? "")=\($0.value)" })

This keeps debug tooling useful without leaking sensitive values in logs.

Common Pitfalls

  • Overusing Mirror for core application logic and hurting readability.
  • Expecting runtime mutation and dynamic invocation similar to scripting languages.
  • Relying on reflected property order for business behavior.
  • Mixing Objective-C runtime techniques into pure Swift modules unnecessarily.
  • Ignoring performance overhead in hot paths.

Summary

  • Swift supports reflection mainly through Mirror.
  • Reflection in Swift is useful for inspection, debugging, and tooling.
  • Swift intentionally avoids fully dynamic runtime mutation patterns.
  • Protocols and generics are usually better for production architecture.
  • Use Objective-C runtime interop only when compatibility requirements justify it.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.