What are the macro roles, and which real macros use which?

5.5.2 · L5 · Learning

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 addsExample
freestanding expressiona value at the use site#Predicate, #expect
freestanding declarationdeclarations where it is written#Preview
attached peera sibling declaration@Test
attached membermembers inside the type@Observable, @Model
attached memberAttributeattributes on existing members@Observable
attached accessorgetters and setterstracked properties
attached extensionconformances 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 @Observable is magic. It is three roles generating ordinary code you can expand and read.

They might follow up with

  • How do you see what @Observable generates? 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…