An Early Look at Swift Macros

Here's an early look at Swift Macros, a feature introduced at WWDC 2023 to reduce boilerplate code and make repetitive Swift patterns easier to manage.
Written by

Iñaki Narciso

Updated on

Apr 06 2026

Table of contents

    Overview

    Writing Swift can sometimes introduce a lot of ceremony. Repeated setup, protocol requirements, and mechanical conversions are the kinds of patterns developers often call boilerplate.

    Swift Macros give developers a way to generate some of that repetitive code at compile time. They were introduced around WWDC 2023, and they opened the door for new Swift and SwiftUI features that feel built into the language while still being distributed as packages.

    What Are Swift Macros?

    Before macros, adding new language behavior generally required a Swift Evolution proposal, discussion, implementation in the compiler, and release through a future toolchain. That process is important for the language itself, but it is not designed for every project-specific pattern.

    A Swift macro lets a package author define code transformations that run during compilation. In practice, that means a macro can expand a small annotation or expression into more complete Swift code.

    Macros are especially useful when a pattern is important but repetitive: generating protocol conformance helpers, creating memberwise code, reducing async completion-handler boilerplate, or adding declarations that would otherwise be easy to mistype.

    Creating a Swift Macro Package

    Swift macros can be created as Swift packages in Xcode. In the menu bar, choose File > New > Package.

    Xcode File New Package menu for creating a Swift package

    From the package templates, choose Swift Macro.

    Xcode Swift Macro package template

    Give the package a name, then create the project. The original example used a package named WWDC.

    Naming a Swift macro package in Xcode

    Xcode creates a package that includes an example #stringify macro, similar to the one demonstrated in Apple's WWDC23 session Write Swift Macros.

    Xcode project navigator showing a generated Swift macro package

    Exploring the Stringify Macro

    Open main.swift from the client target to see how the starter macro is used.

    Xcode editor showing a sample main.swift file using the stringify macro

    The #stringify macro accepts a Swift expression such as a + b. It returns both the evaluated result and a string version of the expression.

    let a = 17
    let b = 25
    
    let result = #stringify(a + b)
    print("The value \(result.1) is \(result.0)")

    Macros feel a little like functions because they accept arguments. Under the hood, though, they run as compiler plugins that inspect syntax and produce expanded Swift source.

    To inspect the generated code, right-click the macro use in Xcode and choose Expand Macro.

    Xcode context menu showing the Expand Macro command

    Xcode then shows the code produced by the macro expansion.

    Xcode editor showing expanded code generated by a Swift macro

    Why Use Macros?

    Macros extend Swift in a controlled way. They let developers reduce repetitive boilerplate while keeping the final expanded code visible to the compiler.

    A useful way to think about macros is to compare them to derived protocol conformances. For example, this model conforms to Codable without manually writing every encoding and decoding detail:

    struct StarSystem: Codable {
        var name, description: String
        var galaxy: Galaxy
        var planets: [Planet]
    }

    Swift can synthesize the required Encodable and Decodable behavior for you. Conceptually, the compiler fills in code like this:

    struct StarSystem: Codable {
        var name, description: String
        var galaxy: Galaxy
        var planets: [Planet]
    
        private enum CodingKeys: String, CodingKey {
            case name, description, galaxy, planets
        }
    
        init(from decoder: Decoder) throws { ... }
        func encode(to encoder: Encoder) throws { ... }
    }

    That generated code is not the exact same mechanism as macros, but the motivation is similar: you write the intent once and let tooling handle the mechanical work.

    Two Kinds of Swift Macros

    Swift supports two broad kinds of macros: freestanding macros and attached macros.

    A freestanding macro appears as an expression or declaration and starts with #.

    return #stringify(a + b)

    An attached macro is written as an attribute on a declaration and starts with @.

    @AddCompletionHandler
    func sendRequest() async throws -> Response

    Quick Reference

    Macro FormWhat It Does
    #stringify(a + b)Uses a freestanding expression macro
    @AddCompletionHandlerUses an attached macro on a declaration
    Expand MacroShows the Swift code Xcode generates from the macro
    Swift Package ManagerDistributes macro implementations as Swift packages

    Summary

    • Swift macros generate Swift code during compilation.
    • They help reduce boilerplate while keeping the expanded code inspectable.
    • Xcode can create a starter Swift Macro package with a sample #stringify macro.
    • Freestanding macros start with #.
    • Attached macros start with @ and decorate declarations.
    • Macros are part of the broader Swift tooling story behind modern Swift and SwiftUI improvements.

    Swift macros are still an advanced topic, but the core idea is approachable: use a small piece of syntax to represent a larger pattern, then let the compiler expand it into normal Swift code.