Macros vs property wrappers vs result builders — one sentence each.
Mental model. Wrapper = runtime storage. Builder = compile-time list. Macro = compile-time code generation. Three different layers.
Property wrapper — wraps storage and routes get/set through wrappedValue at runtime.
Result builder — compiles a block of statements into one expression, building a DSL (@ViewBuilder).
Macro — generates or transforms source/AST at compile time (@Observable, #Preview, #expect). It can replace boilerplate that used to need a wrapper, at the cost of toolchain complexity and slower builds.
Common traps
- Calling
@Observablea property wrapper. It is a macro — the most common version of this trick question. - Using a macro where a wrapper is enough. Macros expand into code you must debug; wrappers are simpler and better understood by tooling.
- Expecting macros to run at runtime. They do not exist at runtime; they only produce code.
They might follow up with
- When would you avoid macros? When the team's toolchain is not uniform, when debugging transparency matters, or when a small wrapper or protocol would do.
- What is a freestanding macro vs an attached macro? Freestanding is called like
#foo; attached decorates a declaration like@foo.
Related questions
- 1.6.3 What is a property wrapper? Can you write a custom one?
- 1.6.4 What is projectedValue? Give a real example.
- 1.5.8 What is a result builder (@ViewBuilder, @CommandsBuilder)?
← What does Sendable mean for a Swift type you write? What is ABI stability vs source compatibility vs module stability? →