Macros
30.1 Using Macros (Swift 5.9+)
Freestanding expression #name(...) Produces an expression #URL, #Predicate, #expect #Preview (SwiftUI previews) Adds declarations next to it Async/completion-handler variants Adds getters/setters Property storage macros Adds attributes to members Adds conformances/extensions @Model, @CodingKeys libraries @attached(body) (6.0) Generates/augments function bodies
// Freestanding expression macros start with #
let url = #URL("https://swift.org") // a compile-time validated URL (common custom macro)
print(#function, #file, #line, #column) // built-in literal "macros"
#warning("Implement caching")
#error("Unsupported platform") // (inside #if conditions)
let predicate = #Predicate<User> { $0.age > 18 } // Foundation/SwiftData
#expect(result == 4) // Swift Testing
let regex = #/(\d+)-(\d+)/# // regex literal (not a macro, but compile-time checked)
// Attached macros start with @
@Observable // Observation framework: generates tracking code
final class Store {
var items: [String] = []
}
@Model // SwiftData
final class Note { var title = "" ; init() {} }
@Test func addition() { #expect(1 + 1 == 2) } // Swift Testing30.2 Writing a Macro
Macros run in a sandboxed compiler plugin, operate on syntax (SwiftSyntax), and are type-checked. Their expansions are visible in Xcode ("Expand Macro"). Test them with assertMacroExpansion.
swift package init --type macro --name MyMacros// Declaration (in the library target)
@freestanding(expression)
public macro stringify<T>(_ value: T) -> (T, String) = #externalMacro(module: "MyMacrosMacros", type: "StringifyMacro")
// Implementation (in the macro target, using SwiftSyntax)
import SwiftSyntax
import SwiftSyntaxMacros
import SwiftCompilerPlugin
public struct StringifyMacro: ExpressionMacro {
public static func expansion(of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext) -> ExprSyntax {
guard let argument = node.arguments.first?.expression else {
fatalError("compiler bug: the macro does not have any arguments")
}
return "(\(argument), \(literal: argument.description))"
}
}
@main
struct MyMacrosPlugin: CompilerPlugin {
let providingMacros: [Macro.Type] = [StringifyMacro.self]
}
// Usage
let (value, code) = #stringify(2 + 3) // (5, "2 + 3")