Swift Academy · Lesson

Writing a Freestanding Expression Macro

Implementing a simple #stringify macro with SwiftSyntax and MacroExpansion.

Lesson 2 of 413 steps

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)) // BinaryOperatorExprSyntax

Macro 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 code

Expanding 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().

Free to start

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

  1. Macro Roles: Freestanding vs Attached
  2. Writing a Freestanding Expression Macro
  3. Attached Member Macros for Boilerplate Reduction
  4. Testing and Debugging Macros
← Back to Swift Academy