0Pricing
Swift Academy · Lesson

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

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

  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