Meta Programming
Swift supports limited metaprogramming through reflection, dynamic member lookup, and callable types. Unlike languages with macros or runtime code generation, Swift’s metaprogramming is constrained for safety and performance.
Reflection
Use Mirror
to inspect types at runtime.
Example: Basic Reflection:
swift
struct User {
let id: String
let name: String
let age: Int
}
let user = User(id: "u1", name: "Alice", age: 30)
let mirror = Mirror(reflecting: user)
for child in mirror.children {
print("\(child.label!): \(child.value)")
}
// Output:
// id: u1
// name: Alice
// age: 30
Example: Nested Reflection:
swift
struct Address {
let city: String
}
struct Person {
let name: String
let address: Address
}
let person = Person(name: "Bob", address: Address(city: "New York"))
func printProperties(_ value: Any) {
let mirror = Mirror(reflecting: value)
for child in mirror.children {
if let label = child.label {
print("\(label): ", terminator: "")
if Mirror(reflecting: child.value).children.isEmpty {
print(child.value)
} else {
print("")
printProperties(child.value)
}
}
}
}
printProperties(person)
// Output:
// name: Bob
// address:
// city: New York
Dynamic Member Lookup
Enable dynamic property access with @dynamicMemberLookup
.
Example:
swift
@dynamicMemberLookup
struct DynamicDict {
private var data: [String: Any]
init(data: [String: Any]) {
self.data = data
}
subscript(dynamicMember key: String) -> Any? {
return data[key]
}
}
let dict = DynamicDict(data: ["name": "Charlie", "age": 25])
print(dict.name) // "Charlie"
print(dict.age) // 25
Example: Type-Safe Lookup:
swift
@dynamicMemberLookup
struct JSON {
private var data: [String: Any]
subscript<T>(dynamicMember key: String) -> T? {
return data[key] as? T
}
}
let json = JSON(data: ["score": 95, "active": true])
print(json.score as Int?) // 95
print(json.active as Bool?) // true
Dynamic Callable
Allow dynamic function calls with @dynamicCallable
.
Example:
swift
@dynamicCallable
struct Logger {
func dynamicallyCall(withArguments args: [String]) {
print(args.joined(separator: " "))
}
func dynamicallyCall(withKeywordArguments args: KeyValuePairs<String, String>) {
print(args.map { "\($0): \($1)" }.joined(separator: ", "))
}
}
let log = Logger()
log("Error", "occurred") // "Error occurred"
log(key: "value", another: "test") // "key: value, another: test"