Hi Swifters, Today we will explore the @dynamicCallable
in Swift. Lets say you have a regular Swift struct, but you want to call it like a function. Sounds weird? Well, thats exactly what @dynamicCallable
lets you do! It’s like giving your types superpowers they can pretend to be functions when they are really not. Lets start with
What is @dynamicCallable?
It transforms your types into callable objects. When you mark a type with @dynamicCallable
, Swift allows you to call instances of that type as if they were functions. Behind the scenes, Swift translates these calls into special method calls that you define. To make your type callable, you need to implement one or both of these special methods:
dynamicallyCall(withArguments:)
– For calls with positional arguments likemyThing(1, 2, 3)
dynamicallyCall(withKeywordArguments:)
– For calls with labeled arguments likemyThing(name: "John", age: 25)
Let’s dive into some real examples
Example 1: Using Positional Arguments
Let’s start witha a super simple greeter that says hello:
Before (Normal Way):
struct Greeter {
func sayHello(to name: String) -> String {
return "Hello, \(name)!"
}
}
let greeter = Greeter()
let message = greeter.sayHello(to: "Alice") // "Hello, Alice!"
print(message)
After (With @dynamicCallable):
@dynamicCallable
struct Greeter {
func dynamicallyCall(withArguments names: [String]) -> String {
if names.isEmpty {
return "Hello, World!"
}
return "Hello, \(names[0])!"
}
}
let greeter = Greeter()
let message1 = greeter("Alice") // "Hello, Alice!"
let message2 = greeter() // "Hello, World!"
print(message1)
print(message2)
What happened? We added @dynamicCallable
to our struct, then we wrote a special method dynamicallyCall(withArguments:)
, and now we can call greeter("Alice")
instead of greeter.sayHello(to: "Alice")
. Nice right?
Example 2: Using Labeled Arguments
Now let’s try the second type of dynamic call with labels:
@dynamicCallable
struct PersonCreator {
func dynamicallyCall(withKeywordArguments args: KeyValuePairs<String, String>) -> String {
var person = "Person: "
for (key, value) in args {
person += "\(key) is \(value), "
}
return String(person.dropLast(2)) // Remove last ", "
}
}
let creator = PersonCreator()
let person1 = creator(name: "John")
let person2 = creator(name: "Alice", age: "25")
let person3 = creator(name: "Bob", age: "30", city: "NYC")
print(person1) // "Person: name is John"
print(person2) // "Person: name is Alice, age is 25"
print(person3) // "Person: name is Bob, age is 30, city is NYC"
What’s different here? We used withKeywordArguments
instead of withArguments
and now we can call it with labels: creator(name: "John", age: "25")
. Each argument has a name and a value now.
A Real-World Example: A Simple Logger
Here is something you might actually use in a real app:
@dynamicCallable
struct Logger {
func dynamicallyCall(withArguments messages: [String]) -> Void {
let timestamp = Date()
let fullMessage = messages.joined(separator: " ")
print("[\(timestamp)] \(fullMessage)")
}
func dynamicallyCall(withKeywordArguments args: KeyValuePairs<String, String>) -> Void {
let timestamp = Date()
var logParts: [String] = []
for (key, value) in args {
logParts.append("\(key): \(value)")
}
print("[\(timestamp)] \(logParts.joined(separator: ", "))")
}
}
let logger = Logger()
// Simple logging
logger("App started")
logger("User logged in", "successfully")
// Structured logging
logger(event: "user_login", user: "john123", status: "success")
// Output:
// [2025-06-06 18:45:38 +0000] App started
// [2025-06-06 18:45:38 +0000] User logged in successfully
// [2025-06-06 18:45:38 +0000] event: user_login, user: john123, status: success
When Should You Use @dynamicCallable?
Great for:
- Making APIs feel more natural
- When you want function-like syntax
- Building configuration systems
- Creating domain-specific languages (DSLs)
Not great for:
- Simple, straightforward operations (regular methods are clearer)
- When you need strict type checking at compile time
- When code readability is more important than cool syntax
@dynamicCallable
is like giving your Swift types a new way to be called. It’s not magic, it’s just a cleaner syntax for method calls. The key is to use it when it makes your code more readable and natural, not just because it looks cool.
Alright, that was all for this one. Feel free to contact me on Instagram if you have any additional tips or feedback. I will see you in the next one. Ciao 🙂