Writing a Freestanding Expression Macro
Implementing a simple #stringify macro with SwiftSyntax and MacroExpansion.
Writing a Freestanding Expression Macro is a free Swift Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Swift Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Project Setup
Macros live in a separate Swift package target of kind .macro, using SwiftSyntax.
// Package.swift (excerpt)
.macro(
name: "MyMacros",
dependencies: [
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftCompilerPlugin", package: "swift-syntax")
]
)Macro Declaration
Declare the macro signature in a library target so clients can call it.
@freestanding(expression)
public macro stringify<T>(_ value: T) -> (T, String)
= #externalMacro(module: "MyMacros", type: "StringifyMacro")Implementing the Macro Type
Implement the macro by conforming to ExpressionMacro and returning a ExprSyntax.
import SwiftSyntaxMacros
import SwiftSyntax
struct StringifyMacro: ExpressionMacro {
static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
guard let arg = node.arguments.first?.expression else {
throw MacroError.message("Need an argument")
}
return "(\(arg), \(literal: arg.description))"
}
}Registering the Plugin
Create a CompilerPlugin that lists all macros in the package.
import SwiftCompilerPlugin
@main
struct MyPlugin: CompilerPlugin {
let providingMacros: [Macro.Type] = [
StringifyMacro.self
]
}Using the Macro
Import the macro library and call it with #stringify.
import MyMacros
let (value, source) = #stringify(2 + 3)
print(value) // 5
print(source) // "2 + 3"ExprSyntax Construction
Return values are built using string interpolation on ExprSyntax, leveraging SwiftSyntax's type safety.
// The expansion returns a tuple literal
return "((\(arg)), \(literal: arg.description))"Accessing Macro Arguments
Arguments are accessed via node.arguments, which is a list of labeled expressions.
let args = node.arguments
for arg in args {
print(arg.label?.text ?? "_", arg.expression)
}Generating Diagnostics
Emit compiler warnings or errors from macros using the expansion context.
context.diagnose(Diagnostic(
node: node,
message: MyDiagnostic.missingArgument
))SwiftSyntax Syntax Nodes
SwiftSyntax represents every language construct as a typed syntax node you can inspect and construct.
import SwiftSyntax
let expr: ExprSyntax = "1 + 2"
print(type(of: expr)) // BinaryOperatorExprSyntaxMacro Hygiene
Swift macros are hygienic by default: generated names won't clash with names in the calling scope.
// Macro-generated variables get unique names internally
// preventing accidental shadowing in user codeExpanding in Xcode
Right-click any macro call in Xcode and choose Expand Macro to inspect the generated source.
#stringify(40 + 2)
// Xcode expands to: (40 + 2, "40 + 2")Quick Check
What protocol must an expression macro implementation conform to?
Lesson Recap
Write a freestanding expression macro by: declaring it with @freestanding(expression), implementing ExpressionMacro in a .macro target, registering via CompilerPlugin, and calling with #macroName().
Frequently asked questions
Is the “Writing a Freestanding Expression Macro” lesson free?
Yes — the full text of “Writing a Freestanding Expression Macro” is free to read here on the web, and the Swift Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Swift Academy course, upgrade to CoddyKit PRO.
What will I learn in “Writing a Freestanding Expression Macro”?
Implementing a simple #stringify macro with SwiftSyntax and MacroExpansion. You practise Swift Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Swift Academy?
No prior experience is required. Swift Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Writing a Freestanding Expression Macro” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Swift Academy lesson?
Yes. Every Swift Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Macro Roles: Freestanding vs Attached
- Writing a Freestanding Expression Macro
- Attached Member Macros for Boilerplate Reduction
- Testing and Debugging Macros