Swift Macros: praktische Beispiele für Metaprogrammierung
Vollständiger Leitfaden zu Swift Macros: Erstellung freistehender und attached Makros inklusive Swift 6 Body Macros, AST-Manipulation mit swift-syntax und praktische Beispiele zur Vermeidung von Boilerplate-Code.

Swift Macros verändern die Art, wie Entwickler Code schreiben und pflegen. Mit Swift 5.9 eingeführt und in Swift 6 erheblich erweitert, ermöglichen Makros Code-Generierung zur Compile-Zeit und bewahren dabei die statische Typsicherheit. Anders als die Makros des C-Präprozessors sind Swift Macros typsicher, in den Compiler integriert und werden vollständig von den Entwicklungstools unterstützt, einschließlich der Funktion "Expand Macro" in Xcode.
Dieser Leitfaden behandelt die Erstellung von Swift Macros von den grundlegenden Konzepten bis hin zu fortgeschrittenen Implementierungen, einschließlich der neuen Body Macros aus Swift 6 (SE-0415). Alle Beispiele verwenden Swift 6 und swift-syntax 600.0.0+.
Die Typen von Swift Macros verstehen
Swift bietet zwei Hauptkategorien von Makros mit jeweils eigenen Anwendungsfällen. Freistehende Makros wirken eigenständig als Ausdrücke oder Deklarationen, während attached Makros sich an bestehende Deklarationen binden, um sie zu modifizieren oder zu erweitern.
Freistehende Makros: Expression und Declaration
Freistehende Makros beginnen mit dem Symbol # und können entweder einen Wert zurückgeben (Expression) oder neue Deklarationen erzeugen. Es folgt ein konkretes Beispiel für ein Expression-Makro:
// Freestanding expression macro - generates a value
let buildInfo = #buildDate
// Expansion → "2026-08-23 10:30:45"
// Freestanding macro with arguments
let message = #stringify(1 + 2)
// Expansion → "1 + 2 = 3"
// Freestanding declaration macro - creates declarations
#makeCase("success", "failure", "pending")
// Expansion →
// case success
// case failure
// case pendingDer wesentliche Unterschied zwischen Expression und Declaration liegt im Ergebnis: Eine Expression liefert einen Wert, eine Declaration erzeugt strukturellen Code (Typen, Funktionen, Variablen).
Attached Makros: die sechs Rollen
Attached Makros beginnen mit @ und stehen vor einer Deklaration. Swift 6 definiert sechs unterschiedliche Rollen für diese Makros, einschließlich der neuen body-Rolle für Funktionsimplementierungen:
// @attached(peer) - adds declarations at the same level
@AddAsync
func fetchUser(id: Int) -> User { ... }
// Expansion → adds func fetchUserAsync(id: Int) async -> User
// @attached(accessor) - adds getters/setters
@UserDefault("theme")
var currentTheme: String
// Expansion → adds get { UserDefaults.standard.string(...) }
// @attached(member) - adds members to a type
@AutoEquatable
struct Point {
var x: Int
var y: Int
}
// Expansion → adds static func == (lhs: Point, rhs: Point) -> Bool
// @attached(memberAttribute) - applies attributes to members
@CodableKeys
struct Config {
var apiUrl: String
var timeout: Int
}
// Expansion → adds @CodingKey("api_url") before apiUrl
// @attached(extension) - adds protocol conformances
@Hashable
struct User {
var id: Int
var name: String
}
// Expansion → adds extension User: Hashable { ... }
// @attached(body) - NEW in Swift 6: synthesizes function bodies
@Remote
func fetchData(endpoint: String) -> Data
// Expansion → generates the entire function implementationDiese Rollen lassen sich kombinieren, um leistungsfähige Makros zu erstellen, die Code in mehreren Dimensionen gleichzeitig transformieren.
Swift 6 Body Macros: SE-0415
Swift 6 führte Function Body Macros durch SE-0415 ein, die es ermöglichen, Funktionsimplementierungen zu synthetisieren oder zu ersetzen. Dies adressiert Anwendungsfälle, die keine vorherige Makro-Rolle abdecken konnte.
Das BodyMacro-Protokoll
Das BodyMacro-Protokoll ermöglicht den vollständigen Ersatz eines Funktionskörpers:
import SwiftSyntax
import SwiftSyntaxMacros
public protocol BodyMacro: AttachedMacro {
static func expansion(
of node: AttributeSyntax,
providingBodyFor declaration: some DeclSyntaxProtocol &
WithOptionalCodeBlockSyntax,
in context: some MacroExpansionContext
) throws -> [CodeBlockItemSyntax]
}Das Makro erhält die ursprüngliche Deklaration und gibt Code-Block-Elemente zurück, die zum Funktionskörper werden. Ein vorhandener Körper wird ersetzt.
Implementierung eines Remote Procedure Call Makros
Body Macros sind besonders nützlich, um Netzwerk-Layer-Code aus Funktionssignaturen zu generieren:
import SwiftSyntax
import SwiftSyntaxMacros
public struct RemoteMacro: BodyMacro {
public static func expansion(
of node: AttributeSyntax,
providingBodyFor declaration: some DeclSyntaxProtocol &
WithOptionalCodeBlockSyntax,
in context: some MacroExpansionContext
) throws -> [CodeBlockItemSyntax] {
guard let funcDecl = declaration.as(FunctionDeclSyntax.self) else {
throw MacroError.invalidSyntax("@Remote requires a function")
}
let functionName = funcDecl.name.text
let parameters = funcDecl.signature.parameterClause.parameters
// Generate parameter encoding
let paramDict = parameters.map { param in
let name = param.firstName.text
return "\"\(name)\": \(name)"
}.joined(separator: ", ")
// Generate the RPC body
return [
"""
let params: [String: Any] = [\(raw: paramDict)]
return try await RPCClient.shared.call(
method: "\(raw: functionName)",
params: params
)
"""
]
}
}Deklaration und Verwendung
/// Generates a remote procedure call implementation
@attached(body)
public macro Remote() = #externalMacro(
module: "MyMacrosPlugin",
type: "RemoteMacro"
)class APIService {
@Remote
func createUser(name: String, email: String) async throws -> User
@Remote
func deleteUser(id: UUID) async throws -> Bool
// Each function body is generated at compile time:
// func createUser(name: String, email: String) async throws -> User {
// let params: [String: Any] = ["name": name, "email": email]
// return try await RPCClient.shared.call(
// method: "createUser",
// params: params
// )
// }
}Höchstens ein Body Macro kann auf eine gegebene Funktion angewendet werden. Für kompositionsfähige Preamble-Injektion (Logging, Tracing) wird das PreambleMacro-Protokoll in zukünftigen Swift-Versionen in Betracht gezogen.
Projekteinrichtung für die Erstellung von Makros
Für das Erstellen von Swift Macros wird ein Swift Package mit einer bestimmten Struktur benötigt. Das Package hängt von swift-syntax ab, der offiziellen Bibliothek zur Bearbeitung von Swift-Code als abstrakter Syntaxbaum (AST).
Aufbau der Package.swift
// swift-tools-version: 6.0
import PackageDescription
let package = Package(
name: "MyMacros",
platforms: [.macOS(.v10_15), .iOS(.v13)],
products: [
// Library exposing macros to the main project
.library(
name: "MyMacros",
targets: ["MyMacros"]
),
// Executable for testing macros
.executable(
name: "MyMacrosClient",
targets: ["MyMacrosClient"]
)
],
dependencies: [
// Required dependency for macros - use 600.0.0+ for Swift 6
.package(
url: "https://github.com/swiftlang/swift-syntax.git",
from: "600.0.0"
)
],
targets: [
// Compiler plugin containing implementation
.macro(
name: "MyMacrosPlugin",
dependencies: [
.product(name: "SwiftSyntax", package: "swift-syntax"),
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftCompilerPlugin", package: "swift-syntax")
]
),
// Target exposing macro declarations
.target(
name: "MyMacros",
dependencies: ["MyMacrosPlugin"]
),
// Test client
.executableTarget(
name: "MyMacrosClient",
dependencies: ["MyMacros"]
),
// Unit tests
.testTarget(
name: "MyMacrosTests",
dependencies: [
"MyMacrosPlugin",
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax")
]
)
]
)Diese Konfiguration trennt die Makro-Deklarationen (was der Client-Code sieht) klar von ihrer Implementierung (die zur Compile-Zeit ausgeführt wird).
Mindestens drei Dateien werden benötigt: MyMacros.swift für die Deklarationen, MyMacrosPlugin.swift für die Implementierungen und MyMacrosTests.swift für die Tests. Diese Trennung erleichtert die Wartung.
Ein Expression-Makro erstellen
Expression-Makros erzeugen einen Wert, der direkt im Code verwendbar ist. Hier wird ein #unwrap-Makro erstellt, das ein Optional auspackt und im Fehlerfall den Variablennamen mitliefert. Weitere Informationen zu Swift-Fehlerbehandlungsmustern finden sich im Leitfaden zu Swift Structured Concurrency.
Deklaration des Makros
import Foundation
/// Macro that unwraps an optional with an explicit error message
/// Usage: let value = #unwrap(optionalValue)
/// Expansion: guard let optionalValue else { fatalError("...") }; optionalValue
@freestanding(expression)
public macro unwrap<T>(_ value: T?) -> T = #externalMacro(
module: "MyMacrosPlugin",
type: "UnwrapMacro"
)Die Signatur deklariert, dass das Makro ein Optional entgegennimmt und den nicht-optionalen Wert zurückgibt. #externalMacro verweist auf die Implementierung im Plugin.
Implementierung mit swift-syntax
import SwiftSyntax
import SwiftSyntaxMacros
import SwiftCompilerPlugin
public struct UnwrapMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
// Get the first argument passed to the macro
guard let argument = node.argumentList.first?.expression else {
throw MacroError.missingArgument
}
// Extract the variable name for the error message
let variableName = argument.description.trimmingCharacters(
in: .whitespacesAndNewlines
)
// Generate the expansion code
// Uses an immediately-invoked closure to encapsulate the guard
return """
{
guard let value = \(argument) else {
fatalError("Failed to unwrap '\\(\(literal: variableName))' - value was nil")
}
return value
}()
"""
}
}
// Custom errors for macros
enum MacroError: Error, CustomStringConvertible {
case missingArgument
case invalidSyntax(String)
var description: String {
switch self {
case .missingArgument:
return "The macro requires an argument"
case .invalidSyntax(let message):
return "Invalid syntax: \(message)"
}
}
}Die Methode expansion erhält den AST-Knoten, der den Makroaufruf darstellt, sowie den Compile-Kontext. Sie liefert ein ExprSyntax, das den generierten Code enthält.
Plugin-Registrierung
import SwiftCompilerPlugin
import SwiftSyntaxMacros
@main
struct MyMacrosPlugin: CompilerPlugin {
// List all macros provided by this plugin
let providingMacros: [Macro.Type] = [
UnwrapMacro.self,
RemoteMacro.self,
AutoInitMacro.self,
// Add other macros here
]
}Dieser Einstiegspunkt informiert den Compiler über die im Plugin verfügbaren Makros.
Bereit für deine iOS-Interviews?
Übe mit unseren interaktiven Simulatoren, Flashcards und technischen Tests.
Ein attached Member-Makro erstellen
Member-Makros fügen einem bestehenden Typ Mitglieder hinzu (Eigenschaften, Methoden, verschachtelte Typen). Hier folgt ein @AutoInit-Makro, das automatisch einen Memberwise-Initializer generiert.
Vollständige Deklaration und Implementierung
/// Automatically generates an initializer with all stored properties
@attached(member, names: named(init))
public macro AutoInit() = #externalMacro(
module: "MyMacrosPlugin",
type: "AutoInitMacro"
)import SwiftSyntax
import SwiftSyntaxMacros
public struct AutoInitMacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
// Verify the macro is applied to a struct or class
guard declaration.is(StructDeclSyntax.self) ||
declaration.is(ClassDeclSyntax.self) else {
throw MacroError.invalidSyntax(
"@AutoInit can only be applied to structs and classes"
)
}
// Collect stored properties
let properties = declaration.memberBlock.members
.compactMap { $0.decl.as(VariableDeclSyntax.self) }
.filter { isStoredProperty($0) }
// Generate initializer parameters
let parameters = properties.compactMap { property -> String? in
guard let binding = property.bindings.first,
let identifier = binding.pattern.as(IdentifierPatternSyntax.self),
let type = binding.typeAnnotation?.type else {
return nil
}
let name = identifier.identifier.text
let typeName = type.description.trimmingCharacters(in: .whitespaces)
// Check if the property has a default value
if binding.initializer != nil {
return "\(name): \(typeName) = \(binding.initializer!.value)"
}
return "\(name): \(typeName)"
}
// Generate assignments in the init body
let assignments = properties.compactMap { property -> String? in
guard let binding = property.bindings.first,
let identifier = binding.pattern.as(IdentifierPatternSyntax.self) else {
return nil
}
let name = identifier.identifier.text
return "self.\(name) = \(name)"
}
// Build the complete initializer
let initDecl: DeclSyntax = """
public init(\(raw: parameters.joined(separator: ", "))) {
\(raw: assignments.joined(separator: "\n "))
}
"""
return [initDecl]
}
// Check if a variable is a stored property (not computed)
private static func isStoredProperty(_ variable: VariableDeclSyntax) -> Bool {
guard let binding = variable.bindings.first else { return false }
// A computed property has an accessor block with get/set
if let accessor = binding.accessorBlock {
// If it's a block with explicit accessors, it's computed
if accessor.accessors.is(AccessorDeclListSyntax.self) {
return false
}
}
// let or var without accessor = stored property
return true
}
}Verwendung des AutoInit-Makros
@AutoInit
struct User {
let id: UUID
var name: String
var email: String
var isActive: Bool = true
}
// Automatically generated code:
// public init(id: UUID, name: String, email: String, isActive: Bool = true) {
// self.id = id
// self.name = name
// self.email = email
// self.isActive = isActive
// }
// Usage
let user = User(id: UUID(), name: "Alice", email: "alice@example.com")
// isActive uses the default valueDieses Makro beseitigt das Boilerplate des Initializers und ist besonders bei Datenmodellen mit vielen Eigenschaften nützlich.
Attached Peer-Makro für asynchrone Generierung
Peer-Makros fügen Deklarationen auf derselben Ebene wie die annotierte Deklaration hinzu. Hier folgt ein @AddAsync-Makro, das eine async-Version einer auf Completion-Handler basierenden Funktion generiert. Dieses Muster ist besonders relevant bei der Migration von Legacy-Netzwerkcode, wie im Combine vs async/await Migrationsleitfaden behandelt.
/// Automatically generates an async version of a function with completion handler
@attached(peer, names: suffixed(Async))
public macro AddAsync() = #externalMacro(
module: "MyMacrosPlugin",
type: "AddAsyncMacro"
)import SwiftSyntax
import SwiftSyntaxMacros
public struct AddAsyncMacro: PeerMacro {
public static func expansion(
of node: AttributeSyntax,
providingPeersOf declaration: some DeclSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
// Verify it's a function
guard let funcDecl = declaration.as(FunctionDeclSyntax.self) else {
throw MacroError.invalidSyntax(
"@AddAsync requires a function"
)
}
let functionName = funcDecl.name.text
let asyncFunctionName = "\(functionName)Async"
// Analyze parameters to find the completion handler
let parameters = funcDecl.signature.parameterClause.parameters
// Filter parameters (exclude completion handler)
var regularParams: [String] = []
var completionType: String? = nil
for param in parameters {
let paramType = param.type.description
// Detect a completion handler (closure with Result or simple value)
if paramType.contains("->") && paramType.contains("Void") {
// Extract the return type from completion
completionType = extractCompletionReturnType(from: paramType)
} else {
let paramName = param.firstName.text
let paramSecondName = param.secondName?.text
let label = paramSecondName ?? paramName
regularParams.append("\(paramName): \(paramType)")
}
}
guard let returnType = completionType else {
throw MacroError.invalidSyntax(
"No completion handler found"
)
}
// Generate arguments for internal call
let callArgs = parameters.dropLast().map { param in
let name = param.firstName.text
return "\(name): \(name)"
}.joined(separator: ", ")
// Generate the async function
let asyncFunc: DeclSyntax = """
func \(raw: asyncFunctionName)(\(raw: regularParams.joined(separator: ", "))) async throws -> \(raw: returnType) {
try await withCheckedThrowingContinuation { continuation in
\(raw: functionName)(\(raw: callArgs.isEmpty ? "" : callArgs + ", ")completion: { result in
switch result {
case .success(let value):
continuation.resume(returning: value)
case .failure(let error):
continuation.resume(throwing: error)
}
})
}
}
"""
return [asyncFunc]
}
// Extract return type from a Result type
private static func extractCompletionReturnType(from type: String) -> String {
// Simplified pattern - in production, use the AST
if let match = type.range(of: #"Result<([^,]+)"#, options: .regularExpression) {
var result = String(type[match])
result = result.replacingOccurrences(of: "Result<", with: "")
return result.trimmingCharacters(in: .whitespaces)
}
return "Void"
}
}Demonstration des AddAsync-Makros
class NetworkService {
@AddAsync
func fetchUser(
id: Int,
completion: @escaping (Result<User, Error>) -> Void
) {
// Implementation with callback
URLSession.shared.dataTask(with: URL(string: "/users/\(id)")!) { data, _, error in
if let error = error {
completion(.failure(error))
} else if let data = data {
let user = try? JSONDecoder().decode(User.self, from: data)
completion(.success(user!))
}
}.resume()
}
// Automatically generates:
// func fetchUserAsync(id: Int) async throws -> User {
// try await withCheckedThrowingContinuation { continuation in
// fetchUser(id: id, completion: { result in
// switch result {
// case .success(let value):
// continuation.resume(returning: value)
// case .failure(let error):
// continuation.resume(throwing: error)
// }
// })
// }
// }
}
// Modern usage with async/await
let user = try await networkService.fetchUserAsync(id: 42)Der Name der generierten Funktion muss in names: des @attached-Attributs deklariert werden. Hier zeigt suffixed(Async), dass die generierte Funktion das Suffix "Async" erhält, das an den ursprünglichen Namen angehängt wird.
Unit-Tests für Makros
Das Testen von Makros ist unverzichtbar, da sie Code erzeugen, der anschließend kompiliert wird. Swift stellt SwiftSyntaxMacrosTestSupport bereit, um diese Tests zu erleichtern. Ab swift-syntax 603.0.0+ wird neben XCTest auch Swift Testing unterstützt.
import SwiftSyntaxMacros
import SwiftSyntaxMacrosTestSupport
import XCTest
@testable import MyMacrosPlugin
final class MyMacrosTests: XCTestCase {
// Dictionary of macros to test
let testMacros: [String: Macro.Type] = [
"unwrap": UnwrapMacro.self,
"AutoInit": AutoInitMacro.self,
"AddAsync": AddAsyncMacro.self,
"Remote": RemoteMacro.self
]
func testUnwrapMacroExpansion() throws {
assertMacroExpansion(
"""
let value = #unwrap(optionalString)
""",
expandedSource: """
let value = {
guard let value = optionalString else {
fatalError("Failed to unwrap 'optionalString' - value was nil")
}
return value
}()
""",
macros: testMacros
)
}
func testAutoInitMacroWithStruct() throws {
assertMacroExpansion(
"""
@AutoInit
struct Point {
let x: Int
var y: Int
}
""",
expandedSource: """
struct Point {
let x: Int
var y: Int
public init(x: Int, y: Int) {
self.x = x
self.y = y
}
}
""",
macros: testMacros
)
}
func testAutoInitWithDefaultValues() throws {
assertMacroExpansion(
"""
@AutoInit
struct Config {
var timeout: Int = 30
var retryCount: Int
}
""",
expandedSource: """
struct Config {
var timeout: Int = 30
var retryCount: Int
public init(timeout: Int = 30, retryCount: Int) {
self.timeout = timeout
self.retryCount = retryCount
}
}
""",
macros: testMacros
)
}
func testAutoInitFailsOnEnum() throws {
assertMacroExpansion(
"""
@AutoInit
enum Status {
case active
}
""",
expandedSource: """
enum Status {
case active
}
""",
diagnostics: [
DiagnosticSpec(
message: "@AutoInit can only be applied to structs and classes",
line: 1,
column: 1
)
],
macros: testMacros
)
}
func testRemoteMacroExpansion() throws {
assertMacroExpansion(
"""
@Remote
func fetchUser(id: Int) async throws -> User
""",
expandedSource: """
func fetchUser(id: Int) async throws -> User {
let params: [String: Any] = ["id": id]
return try await RPCClient.shared.call(
method: "fetchUser",
params: params
)
}
""",
macros: testMacros
)
}
}Die Tests prüfen die korrekte Code-Expansion sowie passende Fehlermeldungen bei unzulässiger Verwendung.
Makros debuggen und untersuchen
Xcode bietet mehrere Werkzeuge, um Makros zu debuggen und den generierten Code nachzuvollziehen.
Expansion in Xcode
// Right-click on macro call → "Expand Macro"
// Displays generated code inline
@AutoInit
struct Product {
let id: UUID
var name: String
var price: Decimal
}
// To see the expansion:
// 1. Right-click on @AutoInit
// 2. Select "Expand Macro"
// 3. Generated code displays inline for inspection and debuggingLogging während der Entwicklung
public struct DebugMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
// Print the node's AST to understand the structure
print("=== DEBUG MACRO ===")
print("Node: \(node)")
print("Arguments: \(node.argumentList)")
// Complete dump of the syntax tree
dump(node)
// Continue with normal expansion
return "42"
}
}AST mit swift-ast-explorer erkunden
Das Online-Tool swift-ast-explorer.com zeigt den Syntaxbaum eines beliebigen Swift-Codes an. Das Verständnis der AST-Knotenstruktur ist beim Implementieren von Makros unverzichtbar, wie im Swift Testing Framework Leitfaden gezeigt wird, der die in Swift eingebauten Test-Makros behandelt.
Best Practices für Swift Macros
Wartbare Makros entstehen nur, wenn bestimmte Konventionen eingehalten und typische Stolperfallen vermieden werden.
Validierung und Fehlermeldungen
public struct ValidatedMacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
// Validate usage context
guard declaration.is(StructDeclSyntax.self) else {
// Clear error messages with possible localization
context.diagnose(
Diagnostic(
node: node,
message: MacroDiagnosticMessage(
id: "invalid-target",
message: "This macro can only be applied to structs",
severity: .error
)
)
)
return []
}
// Check required arguments
guard let arguments = node.arguments else {
context.diagnose(
Diagnostic(
node: node,
message: MacroDiagnosticMessage(
id: "missing-args",
message: "Required arguments missing",
severity: .error
)
)
)
return []
}
// Implementation...
return []
}
}
// Structure for diagnostic messages
struct MacroDiagnosticMessage: DiagnosticMessage {
let id: String
let message: String
let severity: DiagnosticSeverity
var diagnosticID: MessageID {
MessageID(domain: "MyMacros", id: id)
}
}Lesbaren Code erzeugen
// Avoid: hard-to-read generated code
let badCode: DeclSyntax = "public init(a:Int,b:String,c:Bool){self.a=a;self.b=b;self.c=c}"
// Preferred: properly formatted generated code
let goodCode: DeclSyntax = """
public init(
a: Int,
b: String,
c: Bool
) {
self.a = a
self.b = b
self.c = c
}
"""Der generierte Code sollte genauso lesbar sein wie handgeschriebener Code, da er von Entwicklerinnen und Entwicklern über "Expand Macro" inspiziert wird.
Sources
- SE-0415: Function Body Macros, implementiert in Swift 6.0
- swift-syntax releases, Version 600.0.0+ für Swift 6
- Swift.org Blog, Swift 6.3 Release Notes
Was Swift Macros in der Praxis ermöglichen
Swift Macros sind ein leistungsstarkes Werkzeug, um Boilerplate zu beseitigen, ohne die statische Typsicherheit aufzugeben. Diese Technologie ermöglicht:
- Zwei Kategorien von Makros: freistehend (
#) und attached (@) - Sechs attached-Rollen in Swift 6: peer, accessor, member, memberAttribute, extension und die neue body-Rolle
- Implementierung über swift-syntax 600.0.0+ und AST-Manipulation
- Pflichttests mit
SwiftSyntaxMacrosTestSupport, jetzt mit Swift Testing Unterstützung - Eigenes Package für die Implementierungen erforderlich
- Debugging über "Expand Macro" in Xcode
- Aussagekräftige Fehlermeldungen sind entscheidend für die Developer Experience
Swift Macros sind besonders nützlich, um Konformanzen (Equatable, Codable) zu erzeugen, fortgeschrittene Property Wrapper zu bauen, RPC-Implementierungen aus Signaturen zu synthetisieren und auf Callbacks basierende APIs in Richtung async/await zu modernisieren.
Fang an zu üben!
Teste dein Wissen mit unseren Interview-Simulatoren und technischen Tests.
Findest du den Bug in iOS?
Ein echter Codeausschnitt, ein versteckter Bug, ein Versuch pro Tag. Zum Ausprobieren ohne Konto.

Geschrieben von
Anthony Fillion-MailletGründer von SharpSkill
Seit über 10 Jahren Fullstack-Entwickler. Er leitet SharpSkill und verantwortet alles, was hier erscheint.
Aktualisiert am 23. August 2026
Tags
Teilen
Verwandte Artikel

iOS-Entwickler Jobs 2026: Jobbörsen, Gehaltsübersicht und Interviewvorbereitung
Der Arbeitsmarkt für iOS-Entwickler 2026: Wo Stellenangebote zu finden sind, aktuelle Gehaltsspannen und wie sich Bewerber optimal auf technische Interviews vorbereiten.

Swift Package Manager 2026: Pakete erstellen, veröffentlichen und Interview-Fragen meistern
Umfassende Anleitung zum Swift Package Manager für iOS-Entwickler. Pakete erstellen, Abhängigkeiten verwalten, Bibliotheken veröffentlichen und häufige SPM-Interview-Fragen beantworten.

Combine vs async/await in Swift: Progressive Migrationsmuster
Vollständiger Leitfaden zur Migration von Combine zu async/await in Swift: progressive Strategien, Bridging-Muster und Paradigmen-Koexistenz in iOS-Codebasen.