swiftgen-integration
SKILL.md
SwiftGen Integration — Expert Decisions
Expert decision frameworks for SwiftGen choices. Claude knows asset catalogs and localization — this skill provides judgment calls for when SwiftGen adds value and configuration trade-offs.
Decision Trees
When SwiftGen Adds Value
Should you use SwiftGen for this project?
├─ > 20 assets/strings
│ └─ YES — Type safety prevents bugs
│ Typos caught at compile time
│
├─ < 10 assets/strings, solo developer
│ └─ MAYBE — Overhead vs. benefit
│ Quick projects may not need it
│
├─ Team project with shared assets
│ └─ YES — Consistency + discoverability
│ Autocomplete reveals available assets
│
├─ Assets change frequently
│ └─ YES — Broken references caught early
│ CI catches missing assets
│
└─ CI/CD pipeline exists
└─ YES — Validate assets on every build
Prevents runtime crashes
The trap: Using SwiftGen on tiny projects or for assets that rarely change. The setup overhead may exceed the benefit.
Template Selection
Which template should you use?
├─ Strings
│ ├─ Hierarchical keys (auth.login.title)
│ │ └─ structured-swift5
│ │ L10n.Auth.Login.title
│ │
│ └─ Flat keys (login_title)
│ └─ flat-swift5
│ L10n.loginTitle
│
├─ Assets (Images)
│ └─ swift5 (default)
│ Asset.Icons.home.image
│
├─ Colors
│ └─ swift5 with enumName param
│ Asset.Colors.primary.color
│
├─ Fonts
│ └─ swift5
│ FontFamily.Roboto.bold.font(size:)
│
└─ Storyboards
└─ scenes-swift5
StoryboardScene.Main.initialViewController()
Asset Organization Strategy
How should you organize assets?
├─ Small app (< 50 assets)
│ └─ Single Assets.xcassets
│ Feature folders inside catalog
│
├─ Medium app (50-200 assets)
│ └─ Feature-based catalogs
│ Auth.xcassets, Dashboard.xcassets
│ Multiple swiftgen inputs
│
├─ Large app / multi-module
│ └─ Per-module asset catalogs
│ Each module owns its assets
│ Module-specific SwiftGen runs
│
└─ Design system / shared assets
└─ Separate DesignSystem.xcassets
Shared across targets
Build Phase Strategy
When should SwiftGen run?
├─ Every build
│ └─ Run Script phase (before Compile Sources)
│ Always current, small overhead
│
├─ Only when assets change
│ └─ Input/Output files specified
│ Xcode skips if unchanged
│
├─ Manual only (CI generates)
│ └─ Commit generated files
│ No local SwiftGen needed
│ Risk: generated files out of sync
│
└─ Pre-commit hook
└─ Lint + generate before commit
Ensures consistency
NEVER Do
Configuration
NEVER hardcode paths without variables:
# ❌ Breaks in different environments
strings:
inputs: /Users/john/Projects/MyApp/Resources/en.lproj/Localizable.strings
outputs:
output: /Users/john/Projects/MyApp/Generated/Strings.swift
# ✅ Use relative paths
strings:
inputs: Resources/en.lproj/Localizable.strings
outputs:
output: Generated/Strings.swift
NEVER forget publicAccess for shared modules:
# ❌ Generated code is internal — can't use from other modules
xcassets:
inputs: Resources/Assets.xcassets
outputs:
- templateName: swift5
output: Generated/Assets.swift
# Missing publicAccess!
# ✅ Add publicAccess for shared code
xcassets:
inputs: Resources/Assets.xcassets
outputs:
- templateName: swift5
output: Generated/Assets.swift
params:
publicAccess: true # Accessible from other modules
Generated Code Usage
NEVER use string literals alongside SwiftGen:
// ❌ Defeats the purpose
let icon = UIImage(named: "home") // String literal!
let title = NSLocalizedString("auth.login.title", comment: "") // String literal!
// ✅ Use generated constants everywhere
let icon = Asset.Icons.home.image
let title = L10n.Auth.Login.title
NEVER modify generated files:
// ❌ Changes will be overwritten
// Generated/Assets.swift
enum Asset {
enum Icons {
static let home = ImageAsset(name: "home")
// My custom addition <- WILL BE DELETED ON NEXT RUN
static let customIcon = ImageAsset(name: "custom")
}
}
// ✅ Extend in separate file
// Extensions/Asset+Custom.swift
extension Asset.Icons {
// Extensions survive regeneration
}
Build Phase
NEVER put SwiftGen after Compile Sources:
# ❌ Generated files don't exist when compiling
Build Phases order:
1. Compile Sources <- Fails: Assets.swift doesn't exist!
2. Run Script (SwiftGen)
# ✅ Generate before compiling
Build Phases order:
1. Run Script (SwiftGen) <- Generates Assets.swift
2. Compile Sources <- Now Assets.swift exists
NEVER skip SwiftGen availability check:
# ❌ Build fails if SwiftGen not installed
swiftgen config run # Error: command not found
# ✅ Check availability, warn instead of fail
if which swiftgen >/dev/null; then
swiftgen config run --config "$SRCROOT/swiftgen.yml"
else
echo "warning: SwiftGen not installed, skipping code generation"
fi
Version Control
NEVER commit generated files without good reason:
# ❌ Merge conflicts, stale files
git add Generated/Assets.swift
git add Generated/Strings.swift
# ✅ Gitignore generated files
# .gitignore
Generated/
*.generated.swift
# Exception: If CI doesn't run SwiftGen, commit generated files
# But then add pre-commit hook to keep them fresh
NEVER leave swiftgen.yml uncommitted:
# ❌ Team members can't regenerate
.gitignore
swiftgen.yml <- WRONG!
# ✅ Commit configuration
git add swiftgen.yml
git add Resources/ # Source assets
String Keys
NEVER use inconsistent key conventions:
# ❌ Mixed conventions — confusing
"LoginTitle" = "Log In";
"login.button" = "Sign In";
"AUTH_ERROR" = "Error";
# ✅ Consistent hierarchical keys
"auth.login.title" = "Log In";
"auth.login.button" = "Sign In";
"auth.error.generic" = "Error";
Essential Patterns
Complete swiftgen.yml
# swiftgen.yml
## Strings (Localization)
strings:
inputs:
- Resources/en.lproj/Localizable.strings
outputs:
- templateName: structured-swift5
output: Generated/Strings.swift
params:
publicAccess: true
enumName: L10n
## Assets (Images)
xcassets:
- inputs:
- Resources/Assets.xcassets
outputs:
- templateName: swift5
output: Generated/Assets.swift
params:
publicAccess: true
## Colors
colors:
- inputs:
- Resources/Colors.xcassets
outputs:
- templateName: swift5
output: Generated/Colors.swift
params:
publicAccess: true
enumName: ColorAsset
## Fonts
fonts:
- inputs:
- Resources/Fonts/
outputs:
- templateName: swift5
output: Generated/Fonts.swift
params:
publicAccess: true
SwiftUI Convenience Extensions
// Extensions/SwiftGen+SwiftUI.swift
import SwiftUI
// Image extension
extension Image {
init(asset: ImageAsset) {
self.init(asset.name, bundle: BundleToken.bundle)
}
}
// Color extension
extension Color {
init(asset: ColorAsset) {
self.init(asset.name, bundle: BundleToken.bundle)
}
}
// Font extension
extension Font {
static func custom(_ fontConvertible: FontConvertible, size: CGFloat) -> Font {
fontConvertible.swiftUIFont(size: size)
}
}
// Usage
struct ContentView: View {
var body: some View {
VStack {
Image(asset: Asset.Icons.home)
.foregroundColor(Color(asset: Asset.Colors.primary))
Text(L10n.Home.title)
.font(.custom(FontFamily.Roboto.bold, size: 24))
}
}
}
Build Phase Script
#!/bin/bash
# Xcode Build Phase: Run Script
# Move BEFORE "Compile Sources"
set -e
# Check if SwiftGen is installed
if ! which swiftgen >/dev/null; then
echo "warning: SwiftGen not installed. Install via: brew install swiftgen"
exit 0
fi
# Navigate to project root
cd "$SRCROOT"
# Create output directory if needed
mkdir -p Generated
# Run SwiftGen
echo "Running SwiftGen..."
swiftgen config run --config swiftgen.yml
echo "SwiftGen completed successfully"
Input Files (for incremental builds):
$(SRCROOT)/swiftgen.yml
$(SRCROOT)/Resources/Assets.xcassets
$(SRCROOT)/Resources/en.lproj/Localizable.strings
$(SRCROOT)/Resources/Colors.xcassets
$(SRCROOT)/Resources/Fonts
Output Files:
$(SRCROOT)/Generated/Assets.swift
$(SRCROOT)/Generated/Strings.swift
$(SRCROOT)/Generated/Colors.swift
$(SRCROOT)/Generated/Fonts.swift
Multi-Module Setup
# Module: DesignSystem/swiftgen.yml
xcassets:
- inputs:
- Sources/DesignSystem/Resources/Colors.xcassets
outputs:
- templateName: swift5
output: Sources/DesignSystem/Generated/Colors.swift
params:
publicAccess: true # Must be public for cross-module
# Module: Feature/swiftgen.yml
strings:
- inputs:
- Sources/Feature/Resources/en.lproj/Feature.strings
outputs:
- templateName: structured-swift5
output: Sources/Feature/Generated/Strings.swift
params:
publicAccess: false # Internal to module
enumName: Strings
Quick Reference
Template Options
| Asset Type | Template | Output |
|---|---|---|
| Images | swift5 | Asset.Category.name.image |
| Colors | swift5 | Asset.Colors.name.color |
| Strings | structured-swift5 | L10n.Category.Subcategory.key |
| Strings (flat) | flat-swift5 | L10n.keyName |
| Fonts | swift5 | FontFamily.Name.weight.font(size:) |
| Storyboards | scenes-swift5 | StoryboardScene.Name.viewController |
Common Parameters
| Parameter | Purpose | Example |
|---|---|---|
| publicAccess | Public access level | true for shared modules |
| enumName | Custom enum name | L10n, Asset, Colors |
| allValues | Include allValues array | true for debugging |
| preservePath | Keep folder structure | true for fonts |
File Structure
Project/
├── swiftgen.yml # Configuration (commit)
├── Resources/
│ ├── Assets.xcassets # Images (commit)
│ ├── Colors.xcassets # Colors (commit)
│ ├── Fonts/ # Custom fonts (commit)
│ └── en.lproj/
│ └── Localizable.strings # Strings (commit)
└── Generated/ # Output (gitignore)
├── Assets.swift
├── Colors.swift
├── Fonts.swift
└── Strings.swift
Troubleshooting
| Issue | Cause | Fix |
|---|---|---|
| "No such module" | Generated before adding to target | Add to target membership |
| Build fails | Run Script after Compile | Move before Compile Sources |
| Stale generated code | Missing input/output files | Specify all inputs/outputs |
| Wrong bundle | Multi-target project | Use correct BundleToken |
Red Flags
| Smell | Problem | Fix |
|---|---|---|
| String literals for assets | Bypasses type safety | Use generated constants |
| Modified generated files | Changes get overwritten | Use extensions instead |
| Run Script after Compile | Files don't exist | Move before Compile Sources |
| No availability check | Build fails without SwiftGen | Add which swiftgen check |
| Committed generated files | Merge conflicts, staleness | Gitignore, generate on build |
| Missing publicAccess | Can't use across modules | Add publicAccess: true |
| Mixed key conventions | Inconsistent L10n structure | Use hierarchical keys |
Weekly Installs
15
Repository
kaakati/rails-e…rise-devGitHub Stars
6
First Seen
Jan 25, 2026
Security Audits
Installed on
claude-code13
opencode12
gemini-cli11
codex11
antigravity10
github-copilot10