--- id: ios-swift-macros-deep title: Swift Macros β€” compile-time codegen category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [ios, swift, vibe-coding] tech_stack: { language: "Swift", applicable_to: ["iOS"] } applied_in: [] aliases: [Swift Macros, freestanding macro, attached macro, SwiftSyntax, codegen] --- # Swift Macros > Swift 5.9+ 의 hygenic codegen. **Freestanding (`#stringify(x)`) + attached (`@Observable`)**. Boilerplate ↓. ## πŸ“– 핡심 κ°œλ… - Compile-time expansion (no runtime). - Hygenic (scope 격리). - SwiftSyntax κ°€ backbone. - Type-safe. ## πŸ’» μ½”λ“œ νŒ¨ν„΄ ### Freestanding macro ```swift let (result, source) = #stringify(2 + 3) print(result) // 5 print(source) // "(2 + 3)" ``` β†’ `#stringify` κ°€ expression + source string. ### @Observable (Apple) ```swift import Observation @Observable class UserModel { var name: String = "" var age: Int = 0 } // Auto-generates: // - Tracking infrastructure // - withMutation calls // - Observer registration ``` β†’ SwiftUI κ°€ μžλ™ observe. ### @Model (SwiftData) ```swift import SwiftData @Model class Item { var name: String var quantity: Int init(name: String, quantity: Int) { self.name = name self.quantity = quantity } } // Auto-generates: // - Persistence // - Relationships // - Identity ``` ### Custom macro μž‘μ„± ```swift // Package.swift import CompilerPluginSupport import PackageDescription let package = Package( name: "MyMacros", platforms: [.macOS(.v13)], products: [ .library(name: "MyMacros", targets: ["MyMacros"]), ], dependencies: [ .package(url: "https://github.com/apple/swift-syntax.git", from: "509.0.0"), ], targets: [ .macro( name: "MyMacroPlugin", dependencies: [ .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), .product(name: "SwiftCompilerPlugin", package: "swift-syntax"), ] ), .target(name: "MyMacros", dependencies: ["MyMacroPlugin"]), ] ) ``` ### Stringify implementation ```swift import SwiftSyntax import SwiftSyntaxMacros public struct StringifyMacro: ExpressionMacro { public static func expansion( of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext ) -> ExprSyntax { guard let arg = node.arguments.first?.expression else { return "" } return "(\(arg), \(literal: arg.description))" } } ``` ```swift // MyMacros λͺ¨λ“ˆ @freestanding(expression) public macro stringify(_ value: T) -> (T, String) = #externalMacro(module: "MyMacroPlugin", type: "StringifyMacro") ``` ### Attached macro ```swift @AddInit struct User { let name: String let age: Int } // Auto-generates: // init(name: String, age: Int) { // self.name = name // self.age = age // } ``` ```swift public struct AddInitMacro: MemberMacro { public static func expansion(...) throws -> [DeclSyntax] { // Generate init from struct properties. } } ``` ### @Test (Swift Testing) ```swift import Testing @Test func addTwoNumbers() { #expect(1 + 1 == 2) } @Test(arguments: [(1, 2, 3), (5, 5, 10)]) func add(_ a: Int, _ b: Int, _ expected: Int) { #expect(a + b == expected) } ``` β†’ XCTest 의 modern. ### Use case ``` - @Observable: SwiftUI state. - @Model: SwiftData persistence. - @Test: testing. - Custom: builder, Codable boilerplate, lens. β†’ Boilerplate κ°€ 큰 β†’ macro 의 λ‹΅. ``` ### Power 와 cost ``` + Boilerplate μ€„μž„. + Type-safe. + Compile-time error. - Compile time ↑. - Debug 어렀움 (expanded code). - Learning curve. β†’ Library author κ°€ 자주 μž‘μ„±. App dev κ°€ μ‚¬μš© 만 흔함. ``` ### Diagnostics ```swift public static func expansion(...) throws -> [DeclSyntax] { guard let structDecl = declaration.as(StructDeclSyntax.self) else { throw MacroError.notStruct } if structDecl.members.isEmpty { context.diagnose(Diagnostic( node: declaration, message: SimpleDiagnosticMessage( message: "Struct has no members", diagnosticID: .init(domain: "MyMacros", id: "empty"), severity: .error ) )) } // ... } ``` β†’ Compile error in Xcode. ### Expanded code 보기 ``` Xcode Editor β†’ Right-click β†’ Expand Macro. λ˜λŠ”: swift -dump-ast file.swift ``` β†’ Generated code κ²€ν† . ### Conformance macro ```swift @AddCodable struct User { let name: String } // Generates: Codable conformance. ``` β†’ Boilerplate ↓ (Swift κ°€ μžλ™ Codable 도 OK κ°€, custom logic ν•„μš” μ‹œ macro). ### Macro vs property wrapper ``` Property wrapper: - Runtime. - μž‘μ€ logic. - λ§€ access κ°€ cost. Macro: - Compile-time. - 큰 codegen. - 0 runtime cost. β†’ @Published (μ˜› Combine) β†’ @Observable (modern macro). ``` ### Macro vs protocol extension ``` Protocol extension: - λ‹€μ΄λ‚˜λ―Ή dispatch. - λ§€ type κ°€ 자체. Macro: - 정적 codegen. - μ •λ°€ control. β†’ λ§€ use case κ°€ 닀름. ``` ### 함정 ``` - Compile time 폭발 (큰 macro). - Diagnostics κ°€ confusing. - Macro 의 λ³€κ²½ = λͺ¨λ“  user recompile. - Test κ°€ 어렀움 (compile-time). - IDE auto-complete κ°€ 약함. ``` ### Production examples ``` - @Observable (Apple, Swift Observation framework). - @Model (SwiftData). - @Test (Swift Testing). - @AsyncFailable (μ‹€ν—˜). - TCA (The Composable Architecture)의 @Reducer. ``` ### Library author 의 use case ``` - Codable 보닀 μ •λ°€ JSON. - Protocol witness table. - Dependency injection. - DSL builder. - Lens / optic. ``` ### Compile time ``` 큰 macro = 큰 expansion = 큰 compile. - 10 macro Γ— 1000 line = 10000 line generated. - Incremental compile κ°€ partial. ``` β†’ Profile + optimize. ### Macro testing ```swift import SwiftSyntaxMacrosTestSupport func testStringify() { assertMacroExpansion( ''' #stringify(1 + 2) ''', expandedSource: ''' (1 + 2, "1 + 2") ''', macros: ['stringify': StringifyMacro.self] ) } ``` β†’ Snapshot test. ### vs Sourcery / GYB (μ˜›) ``` Sourcery: μ™ΈλΆ€ tool, code generation. GYB: Apple internal. Swift Macros: native, type-safe. β†’ Macro κ°€ modern. ``` ### Future ``` 2026: Macros κ°€ mainstream. - 더 λ§Žμ€ framework κ°€ macro. - TCA / Vapor κ°€ macro 채택. - Codegen ecosystem λ°œλ‹¬. ``` ## πŸ€” μ˜μ‚¬κ²°μ • κΈ°μ€€ | μž‘μ—… | μΆ”μ²œ | |---|---| | State management | @Observable | | Persistence | @Model (SwiftData) | | Testing | @Test | | Boilerplate (init, codable) | Custom macro | | μž‘μ€ codegen | Property wrapper | | μ™ΈλΆ€ tool | Sourcery (legacy) | ## ❌ μ•ˆν‹°νŒ¨ν„΄ - **λͺ¨λ“  κ±° macro**: compile time. - **Diagnostics μ—†μŒ**: bad UX. - **Test μ—†μŒ**: silent break. - **Macro 의 macro**: complexity. - **Big logic in macro**: compile slow. ## πŸ€– LLM ν™œμš© 힌트 - Swift 5.9+ macro. - Apple 의 @Observable / @Model / @Test. - Library author 의 λ‹΅. - SwiftSyntax + SwiftCompilerPlugin. ## πŸ”— κ΄€λ ¨ λ¬Έμ„œ - [[iOS_Swift_Macros]] - [[iOS_SwiftData_Patterns]] - [[iOS_Swift_Concurrency_async_await]]