swiftgen-integration

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

SwiftGen 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 crashes
The 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 consistency

SwiftGen应该何时运行?
├─ 每次构建
│  └─ 运行脚本阶段(在编译源代码之前)
│     始终保持最新,开销极小
├─ 仅在资源变更时
│  └─ 指定输入/输出文件
│     若未变更,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:
```yaml
strings: 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
undefined
xcassets: inputs: Resources/Assets.xcassets outputs: - templateName: swift5 output: Generated/Assets.swift params: publicAccess: true # 可被其他模块访问
undefined

Generated 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.title
NEVER 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:
  1. Compile Sources <- Fails: Assets.swift doesn't exist!
  2. Run Script (SwiftGen)
构建阶段顺序:
  1. 编译源代码 <- 失败:Assets.swift不存在!
  2. 运行脚本(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:
```bash
构建阶段顺序:
  1. 运行脚本(SwiftGen) <- 生成Assets.swift
  2. 编译源代码 <- 此时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
undefined
if which swiftgen >/dev/null; then swiftgen config run --config "$SRCROOT/swiftgen.yml" else echo "warning: SwiftGen not installed, skipping code generation" fi
undefined

Version 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
undefined
git add swiftgen.yml git add Resources/ # 源资源
undefined

String 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
undefined
yaml
undefined

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
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
undefined
fonts:
  • inputs:
    • Resources/Fonts/ outputs:
    • templateName: swift5 output: Generated/Fonts.swift params: publicAccess: true
undefined

SwiftUI 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/bash
bash
#!/bin/bash

Xcode 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
undefined
echo "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
undefined

Multi-Module Setup

多模块配置

yaml
undefined
yaml
undefined

Module: 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 TypeTemplateOutput
Imagesswift5Asset.Category.name.image
Colorsswift5Asset.Colors.name.color
Stringsstructured-swift5L10n.Category.Subcategory.key
Strings (flat)flat-swift5L10n.keyName
Fontsswift5FontFamily.Name.weight.font(size:)
Storyboardsscenes-swift5StoryboardScene.Name.viewController
资源类型模板输出示例
图片swift5Asset.Category.name.image
颜色swift5Asset.Colors.name.color
字符串structured-swift5L10n.Category.Subcategory.key
字符串(扁平化)flat-swift5L10n.keyName
字体swift5FontFamily.Name.weight.font(size:)
故事板scenes-swift5StoryboardScene.Name.viewController

Common Parameters

常用参数

ParameterPurposeExample
publicAccessPublic access leveltrue for shared modules
enumNameCustom enum nameL10n, Asset, Colors
allValuesInclude allValues arraytrue for debugging
preservePathKeep folder structuretrue 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.swift
Project/
├── swiftgen.yml           # 配置文件(需提交)
├── Resources/
│   ├── Assets.xcassets    # 图片资源(需提交)
│   ├── Colors.xcassets    # 颜色资源(需提交)
│   ├── Fonts/             # 自定义字体(需提交)
│   └── en.lproj/
│       └── Localizable.strings  # 字符串资源(需提交)
└── Generated/             # 输出目录(加入.gitignore)
    ├── Assets.swift
    ├── Colors.swift
    ├── Fonts.swift
    └── Strings.swift

Troubleshooting

故障排查

IssueCauseFix
"No such module"Generated before adding to targetAdd to target membership
Build failsRun Script after CompileMove before Compile Sources
Stale generated codeMissing input/output filesSpecify all inputs/outputs
Wrong bundleMulti-target projectUse correct BundleToken
问题原因解决方法
"No such module"生成文件未添加到目标将生成文件添加到目标成员中
构建失败运行脚本在编译之后将脚本移至编译源代码之前
生成代码过期未指定输入/输出文件配置所有输入/输出文件
包错误多目标项目使用正确的BundleToken

Red Flags

危险信号

SmellProblemFix
String literals for assetsBypasses type safetyUse generated constants
Modified generated filesChanges get overwrittenUse extensions instead
Run Script after CompileFiles don't existMove before Compile Sources
No availability checkBuild fails without SwiftGenAdd
which swiftgen
check
Committed generated filesMerge conflicts, stalenessGitignore, generate on build
Missing publicAccessCan't use across modulesAdd publicAccess: true
Mixed key conventionsInconsistent L10n structureUse hierarchical keys
问题表现潜在风险解决方法
使用字符串字面量引用资源绕过类型安全全部使用生成的常量
修改生成的文件变更会被覆盖使用扩展替代直接修改
运行脚本在编译之后编译时文件不存在将脚本移至编译源代码之前
未做可用性检查未安装SwiftGen时构建失败添加
which swiftgen
检查
提交生成的文件合并冲突、文件过期加入.gitignore,在构建时生成
缺少publicAccess跨模块无法使用添加publicAccess: true
混合键命名规范本地化结构不一致使用层级化键
undefined