Metaprogramming in Swift: The Magic of Code That Writes Code

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Metaprogramming in Swift: When Code Starts Thinking for You

\n\n

Have you ever dreamed that your code would write routine things for you? That it would analyze its own structure, generate new methods, and adapt to its environment without your involvement? Welcome to the world of metaprogramming in Swift — a technique that transforms a developer from a simple "code writer" into a "creator of code that creates code."

\n\n

In this article, we will explore advanced Swift techniques that will take your code to the next level. You will learn how to use reflection, dynamic calls, KeyPath, and macros to make applications more flexible and development faster. And yes, all of this will be with real examples, not abstract "Hello, World!"

\n\n

This article is for those who already write confidently in Swift and want to look under the hood of the compiler.

\n\n\n\n

What is Metaprogramming and Why Do You Need It in Swift?

\n\n

Metaprogramming in Swift is the ability of a program to manipulate itself or other programs at compile time or runtime. Simply put, it's when your code analyzes or changes its own structure.

\n\n

Several levels of metaprogramming are available in Swift:

\n
    \n
  • Reflection (Mirror) — viewing an object's properties at runtime.
  • \n
  • KeyPath — references to properties as values.
  • \n
  • Conditional Compilation (#if) — different code for different platforms.
  • \n
  • Macros (Swift 5.9+) — code generation at compile time.
  • \n
  • #selector and perform — dynamic method invocation.
  • \n
\n\n

Why is this needed? Imagine: you need to log all properties of an object for debugging. Instead of writing 20 lines of print for each class, you write one universal function that does it for you. Or automatically generate Decodable for complex JSON. That's the magic of metaprogramming.

\n\n

Reflection with Mirror: Looking Inside Objects

\n\n

The simplest and most powerful technique of metaprogramming in Swift is using Mirror. It allows you to get a list of all properties of an object, their names, and values at runtime.

\n\n

Practical Example: Universal Logger

\n\n

Let's say you have a user model:

\n\n
struct User {\n    let id: Int\n    let name: String\n    let email: String\n    let isActive: Bool\n}\n\nlet user = User(id: 1, name: "Anna", email: "anna@example.com", isActive: true)
\n\n

Usually for logging you write something like:

\n\n
print("User(id: \\(user.id), name: \\(user.name), email: \\(user.email), isActive: \\(user.isActive))")
\n\n

What if you have 10 different models? Use Swift reflection:

\n\n
func dumpObject(_ object: Any) {\n    let mirror = Mirror(reflecting: object)\n    var properties: [String] = []\n    for child in mirror.children {\n        if let label = child.label {\n            properties.append("\\(label): \\(child.value)")\n        }\n    }\n    print("\\(type(of: object)) { \\(properties.joined(separator: ", ")) }")\n}\n\ndumpObject(user)\n// Output: User { id: 1, name: Anna, email: anna@example.com, isActive: true }
\n\n

This works for any structs and classes! You can easily extend this approach for:\n

    \n
  • Deep copying of objects.
  • \n
  • Automatic serialization to a dictionary.
  • \n
  • Comparing two objects by all fields.
  • \n
\n\n

KeyPath: References to Properties as Values

\n\n

KeyPath is one of the most elegant advanced Swift techniques. It allows you to pass a reference to a property as a regular value, and then dynamically read or write it.

\n\n

Example: Sort

Blogs

Book Recommendations