swiftgen-integration
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSwiftGen Integration — Expert Decisions
SwiftGen 集成——专业决策指南
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.
针对SwiftGen各类选择的专业决策框架。Claude熟悉资源目录与本地化——本指南提供关于SwiftGen何时能创造价值以及配置权衡的判断依据。
Decision Trees
决策树
When SwiftGen Adds Value
何时使用SwiftGen能创造价值
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 crashesThe trap: Using SwiftGen on tiny projects or for assets that rarely change. The setup overhead may exceed the benefit.
你的项目是否应该使用SwiftGen?
├─ > 20个资源/字符串
│ └─ 是——类型安全可预防Bug
│ 编译阶段即可捕获拼写错误
│
├─ < 10个资源/字符串,单人开发
│ └─ 可选——权衡开销与收益
│ 小型快速项目可能不需要
│
├─ 共享资源的团队项目
│ └─ 是——保障一致性与可发现性
│ 自动补全可展示可用资源
│
├─ 资源频繁变更
│ └─ 是——提前发现无效引用
│ CI可捕获缺失的资源
│
└─ 存在CI/CD流水线
└─ 是——每次构建时验证资源
避免运行时崩溃误区:在微型项目或极少变更的资源上使用SwiftGen。此时搭建的开销可能超过收益。
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()应该选择哪种模板?
├─ 字符串
│ ├─ 层级化键(auth.login.title)
│ │ └─ structured-swift5
│ │ 示例:L10n.Auth.Login.title
│ │
│ └─ 扁平化键(login_title)
│ └─ flat-swift5
│ 示例:L10n.loginTitle
│
├─ 资源(图片)
│ └─ swift5(默认)
│ 示例:Asset.Icons.home.image
│
├─ 颜色
│ └─ 带enumName参数的swift5
│ 示例:Asset.Colors.primary.color
│
├─ 字体
│ └─ swift5
│ 示例:FontFamily.Roboto.bold.font(size:)
│
└─ 故事板
└─ 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如何组织资源?
├─ 小型应用(<50个资源)
│ └─ 单个Assets.xcassets
│ 目录内按功能划分文件夹
│
├─ 中型应用(50-200个资源)
│ └─ 基于功能的资源目录
│ 示例:Auth.xcassets, Dashboard.xcassets
│ 配置多个SwiftGen输入源
│
├─ 大型应用/多模块项目
│ └─ 按模块划分资源目录
│ 每个模块独立管理自身资源
│ 为每个模块单独运行SwiftGen
│
└─ 设计系统/共享资源
└─ 独立的DesignSystem.xcassets
跨目标共享使用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 consistencySwiftGen应该何时运行?
├─ 每次构建
│ └─ 运行脚本阶段(在编译源代码之前)
│ 始终保持最新,开销极小
│
├─ 仅在资源变更时
│ └─ 指定输入/输出文件
│ 若未变更,Xcode会跳过执行
│
├─ 仅手动运行(由CI生成)
│ └─ 提交生成的文件
│ 本地无需安装SwiftGen
│ 风险:生成的文件可能与源资源不同步
│
└─ 提交前钩子
└─ 提交前执行检查与生成
保障一致性NEVER Do
绝对不要做的事
Configuration
配置
NEVER hardcode paths without variables:
yaml
undefined绝对不要在不使用变量的情况下硬编码路径:
yaml
undefined❌ Breaks in different environments
❌ 在不同环境下会失效
strings:
inputs: /Users/john/Projects/MyApp/Resources/en.lproj/Localizable.strings
outputs:
output: /Users/john/Projects/MyApp/Generated/Strings.swift
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:
```yamlstrings:
inputs: Resources/en.lproj/Localizable.strings
outputs:
output: Generated/Strings.swift
**绝对不要**忘记为共享模块设置publicAccess:
```yaml❌ Generated code is internal — can't use from other modules
❌ 生成的代码为internal——无法在其他模块中使用
xcassets:
inputs: Resources/Assets.xcassets
outputs:
- templateName: swift5
output: Generated/Assets.swift
# Missing publicAccess!
xcassets:
inputs: Resources/Assets.xcassets
outputs:
- templateName: swift5
output: Generated/Assets.swift
# 缺少publicAccess!
✅ Add publicAccess for shared code
✅ 为共享代码添加publicAccess
xcassets:
inputs: Resources/Assets.xcassets
outputs:
- templateName: swift5
output: Generated/Assets.swift
params:
publicAccess: true # Accessible from other modules
undefinedxcassets:
inputs: Resources/Assets.xcassets
outputs:
- templateName: swift5
output: Generated/Assets.swift
params:
publicAccess: true # 可被其他模块访问
undefinedGenerated Code Usage
生成代码的使用
NEVER use string literals alongside SwiftGen:
swift
// ❌ 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.titleNEVER modify generated files:
swift
// ❌ 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
}绝对不要同时使用字符串字面量与SwiftGen:
swift
// ❌ 违背使用SwiftGen的初衷
let icon = UIImage(named: "home") // 字符串字面量!
let title = NSLocalizedString("auth.login.title", comment: "") // 字符串字面量!
// ✅ 全程使用生成的常量
let icon = Asset.Icons.home.image
let title = L10n.Auth.Login.title绝对不要修改生成的文件:
swift
// ❌ 修改内容会在下次运行时被覆盖
// Generated/Assets.swift
enum Asset {
enum Icons {
static let home = ImageAsset(name: "home")
// 我添加的自定义内容 <- 下次运行时会被删除
static let customIcon = ImageAsset(name: "custom")
}
}
// ✅ 在单独文件中扩展
// Extensions/Asset+Custom.swift
extension Asset.Icons {
// 扩展内容不会被生成过程覆盖
}Build Phase
构建阶段
NEVER put SwiftGen after Compile Sources:
bash
undefined绝对不要将SwiftGen放在编译源代码之后:
bash
undefined❌ Generated files don't exist when compiling
❌ 编译时生成的文件还不存在
Build Phases order:
- Compile Sources <- Fails: Assets.swift doesn't exist!
- Run Script (SwiftGen)
构建阶段顺序:
- 编译源代码 <- 失败:Assets.swift不存在!
- 运行脚本(SwiftGen)
✅ Generate before compiling
✅ 在编译前生成文件
Build Phases order:
- Run Script (SwiftGen) <- Generates Assets.swift
- Compile Sources <- Now Assets.swift exists
**NEVER** skip SwiftGen availability check:
```bash构建阶段顺序:
- 运行脚本(SwiftGen) <- 生成Assets.swift
- 编译源代码 <- 此时Assets.swift已存在
**绝对不要**跳过SwiftGen可用性检查:
```bash❌ Build fails if SwiftGen not installed
❌ 若未安装SwiftGen,构建会失败
swiftgen config run # Error: command not found
swiftgen config run # 错误:命令未找到
✅ 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
undefinedif which swiftgen >/dev/null; then
swiftgen config run --config "$SRCROOT/swiftgen.yml"
else
echo "warning: SwiftGen not installed, skipping code generation"
fi
undefinedVersion Control
版本控制
NEVER commit generated files without good reason:
bash
undefined绝对不要无理由提交生成的文件:
bash
undefined❌ Merge conflicts, stale files
❌ 会产生合并冲突、文件过期问题
git add Generated/Assets.swift
git add Generated/Strings.swift
git add Generated/Assets.swift
git add Generated/Strings.swift
✅ Gitignore generated files
✅ 将生成文件加入.gitignore
.gitignore
.gitignore
Generated/
*.generated.swift
Generated/
*.generated.swift
Exception: If CI doesn't run SwiftGen, commit generated files
例外情况:若CI不运行SwiftGen,则提交生成的文件
But then add pre-commit hook to keep them fresh
但需添加提交前钩子以保持文件最新
**NEVER** leave swiftgen.yml uncommitted:
```bash
**绝对不要**不提交swiftgen.yml:
```bash❌ Team members can't regenerate
❌ 团队成员无法重新生成文件
.gitignore
swiftgen.yml <- WRONG!
.gitignore
swiftgen.yml <- 错误!
✅ Commit configuration
✅ 提交配置文件
git add swiftgen.yml
git add Resources/ # Source assets
undefinedgit add swiftgen.yml
git add Resources/ # 源资源
undefinedString Keys
字符串键
NEVER use inconsistent key conventions:
undefined绝对不要使用不一致的键命名规范:
undefined❌ Mixed conventions — confusing
❌ 混合规范——易混淆
"LoginTitle" = "Log In";
"login.button" = "Sign In";
"AUTH_ERROR" = "Error";
"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";
---"auth.login.title" = "Log In";
"auth.login.button" = "Sign In";
"auth.error.generic" = "Error";
---Essential Patterns
核心模式
Complete swiftgen.yml
完整的swiftgen.yml
yaml
undefinedyaml
undefinedswiftgen.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
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
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
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
undefinedfonts:
- inputs:
- Resources/Fonts/ outputs:
- templateName: swift5 output: Generated/Fonts.swift params: publicAccess: true
undefinedSwiftUI Convenience Extensions
SwiftUI 便捷扩展
swift
// 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))
}
}
}swift
// Extensions/SwiftGen+SwiftUI.swift
import SwiftUI
// Image扩展
extension Image {
init(asset: ImageAsset) {
self.init(asset.name, bundle: BundleToken.bundle)
}
}
// Color扩展
extension Color {
init(asset: ColorAsset) {
self.init(asset.name, bundle: BundleToken.bundle)
}
}
// Font扩展
extension Font {
static func custom(_ fontConvertible: FontConvertible, size: CGFloat) -> Font {
fontConvertible.swiftUIFont(size: size)
}
}
// 使用示例
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
构建阶段脚本
bash
#!/bin/bashbash
#!/bin/bashXcode Build Phase: Run Script
Xcode构建阶段:运行脚本
Move BEFORE "Compile Sources"
移至“编译源代码”之前
set -e
set -e
Check if SwiftGen is installed
检查SwiftGen是否已安装
if ! which swiftgen >/dev/null; then
echo "warning: SwiftGen not installed. Install via: brew install swiftgen"
exit 0
fi
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"
cd "$SRCROOT"
Create output directory if needed
若需要则创建输出目录
mkdir -p Generated
mkdir -p Generated
Run SwiftGen
运行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
undefinedecho "Running SwiftGen..."
swiftgen config run --config swiftgen.yml
echo "SwiftGen completed successfully"
**输入文件**(用于增量构建):$(SRCROOT)/swiftgen.yml
$(SRCROOT)/Resources/Assets.xcassets
$(SRCROOT)/Resources/en.lproj/Localizable.strings
$(SRCROOT)/Resources/Colors.xcassets
$(SRCROOT)/Resources/Fonts
**输出文件**:$(SRCROOT)/Generated/Assets.swift
$(SRCROOT)/Generated/Strings.swift
$(SRCROOT)/Generated/Colors.swift
$(SRCROOT)/Generated/Fonts.swift
undefinedMulti-Module Setup
多模块配置
yaml
undefinedyaml
undefinedModule: DesignSystem/swiftgen.yml
模块: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
xcassets:
- inputs:
- Sources/DesignSystem/Resources/Colors.xcassets outputs:
- templateName: swift5 output: Sources/DesignSystem/Generated/Colors.swift params: publicAccess: true # 跨模块使用必须设为public
Module: Feature/swiftgen.yml
模块: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
---strings:
- inputs:
- Sources/Feature/Resources/en.lproj/Feature.strings outputs:
- templateName: structured-swift5 output: Sources/Feature/Generated/Strings.swift params: publicAccess: false # 仅模块内部可访问 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 |
| 资源类型 | 模板 | 输出示例 |
|---|---|---|
| 图片 | swift5 | Asset.Category.name.image |
| 颜色 | swift5 | Asset.Colors.name.color |
| 字符串 | structured-swift5 | L10n.Category.Subcategory.key |
| 字符串(扁平化) | flat-swift5 | L10n.keyName |
| 字体 | swift5 | FontFamily.Name.weight.font(size:) |
| 故事板 | 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 |
| 参数 | 用途 | 示例 |
|---|---|---|
| publicAccess | 访问权限设置 | 共享模块设为true |
| enumName | 自定义枚举名称 | L10n, Asset, Colors |
| allValues | 是否包含allValues数组 | 调试时设为true |
| preservePath | 是否保留文件夹结构 | 字体资源设为true |
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.swiftProject/
├── swiftgen.yml # 配置文件(需提交)
├── Resources/
│ ├── Assets.xcassets # 图片资源(需提交)
│ ├── Colors.xcassets # 颜色资源(需提交)
│ ├── Fonts/ # 自定义字体(需提交)
│ └── en.lproj/
│ └── Localizable.strings # 字符串资源(需提交)
└── Generated/ # 输出目录(加入.gitignore)
├── Assets.swift
├── Colors.swift
├── Fonts.swift
└── Strings.swiftTroubleshooting
故障排查
| 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 |
| 问题 | 原因 | 解决方法 |
|---|---|---|
| "No such module" | 生成文件未添加到目标 | 将生成文件添加到目标成员中 |
| 构建失败 | 运行脚本在编译之后 | 将脚本移至编译源代码之前 |
| 生成代码过期 | 未指定输入/输出文件 | 配置所有输入/输出文件 |
| 包错误 | 多目标项目 | 使用正确的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 |
| 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 |
| 问题表现 | 潜在风险 | 解决方法 |
|---|---|---|
| 使用字符串字面量引用资源 | 绕过类型安全 | 全部使用生成的常量 |
| 修改生成的文件 | 变更会被覆盖 | 使用扩展替代直接修改 |
| 运行脚本在编译之后 | 编译时文件不存在 | 将脚本移至编译源代码之前 |
| 未做可用性检查 | 未安装SwiftGen时构建失败 | 添加 |
| 提交生成的文件 | 合并冲突、文件过期 | 加入.gitignore,在构建时生成 |
| 缺少publicAccess | 跨模块无法使用 | 添加publicAccess: true |
| 混合键命名规范 | 本地化结构不一致 | 使用层级化键 |
undefined