swift-iterable-macros hosts Swift macros that generate iterable collections for your types:
@StaticMemberIterablesynthesizes collections describing everystatic letdefined in a struct, enum, or class.@CaseIterablemirrors Swift’sCaseIterablebut keeps a case’s name, value, and presentation metadata.
This is handy for building fixtures, demo data, menus, or anywhere you want a single source of truth for a handful of well-known static members.
Add the dependency and product to your Package.swift:
.package(url: "https://github.com/davdroman/swift-iterable-macros", from: "0.2.0"),.product(name: "IterableMacros", package: "swift-iterable-macros"),IterableMacros re-exports both modules. If you only need one macro, depend on it explicitly instead:
.product(name: "StaticMemberIterable", package: "swift-iterable-macros"),
.product(name: "CaseIterable", package: "swift-iterable-macros"),import StaticMemberIterable
import SwiftUI
@StaticMemberIterable
enum ColorPalette {
static let sunrise: Color = Color(red: 1.00, green: 0.58, blue: 0.22)
static let moonlight: Color = Color(red: 0.30, green: 0.32, blue: 0.60)
static let stardust: Color = Color(red: 0.68, green: 0.51, blue: 0.78)
}
ColorPalette.allStaticMembers.map(\.value) // [Color(red: 1.00, ...), ...]
ColorPalette.allStaticMembers.map(\.title) // ["Sunrise", "Moonlight", "Stardust"]
ColorPalette.allStaticMembers.map(\.keyPath) // [\ColorPalette.sunrise, ...] as [KeyPath<ColorPalette.Type, Color>]The macro works the same for enums and classes (actors are intentionally unsupported so far).
Each synthesized entry is a StaticMember<Container, Value>: an Identifiable property wrapper that stores the friendly name, the KeyPath to the static property, and the concrete value. This makes it trivial to drive UI:
ForEach(ColorPalette.allStaticMembers) { $color in
Text($color.title)
.padding()
.background {
RoundedRectangle(cornerRadius: 8).fill(color)
}
}StaticMember exposes four pieces of data:
name: String– keeps the original identifier for the member.title: String– human-friendly representation derived from the identifier.keyPath: KeyPath<Container.Type, Value>– points back to the static property inside the declaring type.value/wrappedValue: Value– the actual static instance.
Because it is a property wrapper, you can also project ($member) when you use it on your own properties, and Identifiable conformance makes it slot neatly into ForEach.
import CaseIterable
import SwiftUI
@CaseIterable
@dynamicMemberLookup
enum CoffeeMenu {
case espresso
case cortado
case flatWhite
struct Properties {
let emoji: String
let price: Double
}
var properties: Properties {
switch self {
case .espresso:
Properties(emoji: "☕️", price: 2.50)
case .cortado:
Properties(emoji: "🥛", price: 3.20)
case .flatWhite:
Properties(emoji: "🌿", price: 3.80)
}
}
}
List {
ForEach(CoffeeMenu.allCases) { $coffee in
HStack {
Text("\(coffee.emoji) \($coffee.title)")
Spacer()
Text(coffee.price, format: .currency(code: "USD"))
}
.tag($coffee.id)
}
}@CaseIterable produces an explicit allCases: [CaseOf<Enum>]. Each entry remains a property wrapper (CaseOf) so you keep the friendly title, stable id, and the underlying case value for driving pickers or lists. When you combine the macro with @dynamicMemberLookup plus a nested struct Properties, the generated dynamic-member subscript forwards through properties, letting you ask every case for details such as emoji and price above.
Need public-facing lists? Pass the desired access modifier:
@StaticMemberIterable(.public)
struct Coffee { ... }
@CaseIterable(.public)
enum MenuSection { ... }Supported modifiers:
.public.internal(or omit the argument).package.fileprivate.private
If your namespace stores values of a different type (e.g. an enum that only vends Beverage instances), supply ofType::
@StaticMemberIterable(ofType: Beverage.self)
enum BeverageFixtures {
static let sparkling = Beverage(name: "Sparkling")
static let still = Beverage(name: "Still")
}