What are the macro roles, and which real macros use which?
Predict first
Which role generates a new sibling declaration next to the one it is attached to?
// A: member
// B: peer
// C: accessor
- A
- B
- C
Mental model. Roles are job descriptions: some macros produce a standalone result, others attach to something you wrote and add pieces beside, inside or on top of it.
A macro declares roles that say where it may appear and what it may produce. Freestanding macros are written with a # and stand alone: an expression macro produces a value (#Predicate, #expect), and a declaration macro produces declarations at the use site (#Preview). Attached macros are written with @ on a declaration and add to it: a peer macro adds a sibling declaration (@Test generates the registration code), a member macro adds members to a type, a memberAttribute macro adds attributes to existing members, an accessor macro adds getters and setters to a property, and an extension macro adds conformances and the members that satisfy them.
One macro can have several roles. @Observable combines member (the hidden storage and registrar), memberAttribute (to track each stored property) and extension (the Observable conformance). Knowing the roles tells you what a macro can do: none can delete or rewrite your code, only add around it.
A peer macro adds a declaration beside the original. Member macros add inside a type, and accessor macros add a property’s getter and setter.
| What it adds | Example | |
|---|---|---|
| freestanding expression | a value at the use site | #Predicate, #expect |
| freestanding declaration | declarations where it is written | #Preview |
| attached peer | a sibling declaration | @Test |
| attached member | members inside the type | @Observable, @Model |
| attached memberAttribute | attributes on existing members | @Observable |
| attached accessor | getters and setters | tracked properties |
| attached extension | conformances and their members | @Observable |
Common traps
- Looking for a macro that edits a function body in place. Roles only add; a body role can supply a body for a declaration that has none.
- Assuming
@Observableis magic. It is three roles generating ordinary code you can expand and read.
They might follow up with
- How do you see what
@Observablegenerates? Right-click it in Xcode and choose Expand Macro. - Can a freestanding macro take arguments? Yes, like a function call; the plugin receives them as syntax.
Related questions
- 5.5.1 How does a Swift macro actually run, what can it not do, and what does it cost?
- 1.10.3 Macros vs property wrappers vs result builders — one sentence each.
← How does a Swift macro actually run, what can it not do, and what…