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. This is lesson 2 of 4. Any 3 lessons of this learning path are free to read in full — after that, CoddyKit PRO unlocks every lesson, plus hands-on practice with a built-in code editor and a 24/7 AI tutor. It is part of the Swift Academy learning path, and your progress syncs across the web and the CoddyKit app. The Swift Academy course includes 4 lessons in total.
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().
Learn Swift with an AI tutor — free
Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.
- Courses
- 122
- Lessons
- 409
Frequently Asked Questions
Is the “Writing a Freestanding Expression Macro” lesson free?
Yes — any 3 lessons of the Swift Academy learning path, including “Writing a Freestanding Expression Macro”, are free to read in full here on the web. After that, CoddyKit PRO unlocks every lesson, plus interactive practice with a built-in code editor and a 24/7 AI tutor. The Swift Academy course includes 4 lessons in total.
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, so you can start here or from the beginning and move at your own pace. This is lesson 2 of 4.
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