tca-swiftui-architecture
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseApproach: Production-First Iterative Refactoring — This skill is built for production enterprise codebases using The Composable Architecture. Architecture changes are delivered through iterative refactoring — small, focused PRs tracked in adirectory. AI tools consistently generate outdated TCA code (pre-1.4 patterns, WithViewStore, Environment, Combine Effects). Every rule here exists to prevent those mistakes.refactoring/
方法:生产优先的迭代重构 —— 本技能专为使用The Composable Architecture的企业级生产代码库设计,架构变更通过迭代重构完成:所有小范围、专注的PR都会归类在目录下跟踪。AI工具经常生成过时的TCA代码(1.4版本之前的写法、WithViewStore、Environment、Combine Effects),本技能的所有规则都是为了避免这类错误。refactoring/
TCA SwiftUI Architecture (iOS 16+, TCA 1.7+)
TCA SwiftUI 架构(iOS 16+, TCA 1.7+)
Enterprise-grade skill for The Composable Architecture (point-free/swift-composable-architecture). Opinionated: prescribes macro, , struct-of-closures dependencies, delegate actions for child-parent communication, and modern navigation via /. TCA's API changed substantially across versions — AI tools trained on pre-1.7 code consistently generate , , , and other removed patterns. This skill encodes the modern (1.7+) patterns validated against real enterprise codebases.
@Reducer@ObservableState@PresentsStackStateWithViewStoreIfLetStoreEnvironmentIf the project uses TCA <1.7, consultfor pre-macro patterns and incremental upgrade path.references/migration.md
面向The Composable Architecture(point-free/swift-composable-architecture)的企业级技能。强规约:推荐使用宏、、闭包结构体依赖、父子组件通信的代理action,以及基于/的现代导航方案。TCA的API在不同版本间有较大变动——基于1.7版本之前的代码训练的AI工具经常生成、、等已经被移除的写法,本技能收录了经过真实企业代码库验证的现代(1.7+)TCA写法。
@Reducer@ObservableState@PresentsStackStateWithViewStoreIfLetStoreEnvironment如果项目使用的TCA版本低于1.7,请参考了解宏之前的写法和渐进式升级路径。references/migration.md
Architecture Layers
架构分层
View Layer → SwiftUI Views. Direct store access, NO WithViewStore.
Owns store via let/var. Uses @Bindable for $ bindings.
Reducer Layer → @Reducer struct. State + Action + body. Pure logic.
Returns Effect for async work. ALWAYS runs on main thread.
Effect Layer → .run { send in } for async. .send for sync delegate actions.
Cancellable, mergeable, concatenatable.
Dependency Layer → @DependencyClient structs. Struct-of-closures, NOT protocols.
liveValue (app) / testValue (auto-unimplemented) / previewValue.View Layer → SwiftUI Views. Direct store access, NO WithViewStore.
Owns store via let/var. Uses @Bindable for $ bindings.
Reducer Layer → @Reducer struct. State + Action + body. Pure logic.
Returns Effect for async work. ALWAYS runs on main thread.
Effect Layer → .run { send in } for async. .send for sync delegate actions.
Cancellable, mergeable, concatenatable.
Dependency Layer → @DependencyClient structs. Struct-of-closures, NOT protocols.
liveValue (app) / testValue (auto-unimplemented) / previewValue.Quick Decision Trees
快速决策树
"Should this be its own Feature reducer?"
"这部分逻辑是否应该拆分为独立的Feature reducer?"
Does this component have its own screen/view?
├── YES → Own @Reducer struct
└── NO → Does it have business logic you want to unit test?
├── YES → Own @Reducer struct, compose via Scope
└── NO → Is its state optional (not always visible)?
├── YES → Own @Reducer, use ifLet (avoids unnecessary work)
└── NO → Keep logic in parent reducerDoes this component have its own screen/view?
├── YES → Own @Reducer struct
└── NO → Does it have business logic you want to unit test?
├── YES → Own @Reducer struct, compose via Scope
└── NO → Is its state optional (not always visible)?
├── YES → Own @Reducer, use ifLet (avoids unnecessary work)
└── NO → Keep logic in parent reducer"What navigation pattern do I need?"
"我应该使用哪种导航模式?"
What type of navigation?
├── Modal (sheet/alert/dialog/fullScreenCover)?
│ → Tree-based: @Presents + PresentationAction + ifLet
├── Single drill-down?
│ → Tree-based: optional state + navigationDestination
├── Multi-level push (NavigationStack)?
│ → Stack-based: StackState + StackAction + forEach
├── Deep linking is critical?
│ → Stack-based (easy to construct state array)
└── Modals FROM within a NavigationStack?
→ BOTH: stack for push nav, tree for sheets/alertsWhat type of navigation?
├── Modal (sheet/alert/dialog/fullScreenCover)?
│ → Tree-based: @Presents + PresentationAction + ifLet
├── Single drill-down?
│ → Tree-based: optional state + navigationDestination
├── Multi-level push (NavigationStack)?
│ → Stack-based: StackState + StackAction + forEach
├── Deep linking is critical?
│ → Stack-based (easy to construct state array)
└── Modals FROM within a NavigationStack?
→ BOTH: stack for push nav, tree for sheets/alerts"How should child communicate with parent?"
"子组件应该如何和父组件通信?"
Child needs to tell parent something happened?
├── Use delegate action: case delegate(DelegateAction)
│ Parent observes ONLY .delegate cases, never child internals
│
Child needs to share logic between action cases?
├── Use mutating func on State or private helper method
│ NEVER send an action to share logic — it traverses the entire reducer tree unnecessarily,
│ creating hidden coupling and making performance profiling difficult
│
Sibling reducers need to communicate?
└── Delegate up to parent, parent coordinates
NEVER send actions between siblings directly — siblings should not know about each otherChild needs to tell parent something happened?
├── Use delegate action: case delegate(DelegateAction)
│ Parent observes ONLY .delegate cases, never child internals
│
Child needs to share logic between action cases?
├── Use mutating func on State or private helper method
│ NEVER send an action to share logic — it traverses the entire reducer tree unnecessarily,
│ creating hidden coupling and making performance profiling difficult
│
Sibling reducers need to communicate?
└── Delegate up to parent, parent coordinates
NEVER send actions between siblings directly — siblings should not know about each otherWorkflows
工作流
Workflow: Create a New TCA Feature
工作流:创建新的TCA功能
When: Building a new screen or self-contained feature from scratch.
- Define State as inside
@ObservableState struct— conform to@ReducerEquatable - Define Action enum inside — NO
@Reducerconformance needed (TCA 1.4+)Equatable - Use three-category action pattern: ,
view(ViewAction),internal(InternalAction)(delegate(DelegateAction))rules.md - Implement — use
var body: some ReducerOf<Self>for explicit generics (helps Xcode autocomplete)Reduce<State, Action> { } - Add child features via ,
Scope, orifLetBEFORE parentforEachblockReduce - Create dependencies with macro (
@DependencyClient)dependencies.md - Wire view: direct access,
store.propertyfor@Bindable var storebindings$ - Write exhaustive TestStore tests ()
testing.md
适用场景: 从零开始构建新页面或独立功能。
- 在内部将State定义为
@Reducer—— 遵循@ObservableState struct协议Equatable - 在内部定义Action枚举 —— 不需要遵循
@Reducer协议(TCA 1.4+支持)Equatable - 使用三类action模式:、
view(ViewAction)、internal(InternalAction)(参考delegate(DelegateAction))rules.md - 实现—— 使用
var body: some ReducerOf<Self>显式声明泛型(帮助Xcode自动补全)Reduce<State, Action> { } - 在父代码块之前通过
Reduce、Scope或ifLet引入子功能forEach - 使用宏创建依赖(参考
@DependencyClient)dependencies.md - 视图层对接:直接通过访问属性,使用
store.property实现@Bindable var store绑定$ - 编写覆盖全面的TestStore测试(参考)
testing.md
Workflow: Decompose a God Reducer
工作流:拆解上帝Reducer
When: A single reducer handles logic for multiple screens, has 800+ lines, or testing is impossible to isolate.
- Identify feature boundaries — each screen = own reducer
- Scan for anti-patterns using detection checklist ()
anti-patterns.md - Create directory with per-feature plan files (
refactoring/)refactoring-workflow.md - Extract child reducers bottom-up: leaf features first, then mid-level, then root
- Use for always-present children
Scope(state:action:) - Use for optional children — effects auto-cancel on dismissal
.ifLet(\.$destination, action: \.destination) - Use for stack navigation
.forEach(\.path, action: \.path) - Convert child-parent communication to delegate actions — parent must NEVER observe child internal actions
- Write non-exhaustive integration tests for cross-feature flows ()
testing.md
适用场景: 单个Reducer处理多个页面的逻辑、代码量超过800行,或是无法隔离测试。
- 识别功能边界 —— 每个页面对应独立的reducer
- 使用检查清单扫描反模式(参考)
anti-patterns.md - 创建目录,存放每个功能的重构计划文件(参考
refactoring/)refactoring-workflow.md - 自底向上抽取子reducer:先处理叶子节点功能,再处理中间层,最后处理根节点
- 对始终存在的子组件使用
Scope(state:action:) - 对可选子组件使用—— 组件关闭时副作用会自动取消
.ifLet(\.$destination, action: \.destination) - 对栈导航使用
.forEach(\.path, action: \.path) - 将父子通信逻辑改为代理action —— 父组件绝对不能监听子组件的内部action
- 为跨功能流程编写非全覆盖的集成测试(参考)
testing.md
Workflow: Migrate Legacy TCA to Modern (1.7+)
工作流:将旧版TCA迁移到现代(1.7+)版本
When: Modernizing pre-1.7 TCA code that uses WithViewStore, IfLetStore, Environment, etc.
- Ensure TCA 1.4+ with macro on all reducers (prerequisite)
@Reducer - Migrate feature-by-feature, bottom-up (leaf features first) ()
migration.md - Per feature: add to State AND update view simultaneously — never one without the other
@ObservableState - Replace with
@PresentationState@Presents - Replace with direct
WithViewStore(store, observe: { $0 })accessstore.property - Replace /
IfLetStore/ForEachStorewith native SwiftUI + store.scopeSwitchStore - Replace with
NavigationStackStoreNavigationStack(path: $store.scope(...)) - For iOS 16: wrap views in
WithPerceptionTracking { } - Run existing tests — all must pass before proceeding to next feature
- See full migration checklist and syntax transformations in
migration.md
适用场景: 升级使用WithViewStore、IfLetStore、Environment等写法的1.7版本之前的TCA代码。
- 确保TCA版本不低于1.4,所有reducer都使用宏(前置条件)
@Reducer - 按功能逐个迁移,自底向上推进(先处理叶子节点功能)(参考)
migration.md - 每个功能:给State添加的同时同步更新视图 —— 不能只改其中一端
@ObservableState - 用替换
@Presents@PresentationState - 用直接访问替换
store.propertyWithViewStore(store, observe: { $0 }) - 用原生SwiftUI + store.scope替换/
IfLetStore/ForEachStoreSwitchStore - 用替换
NavigationStack(path: $store.scope(...))NavigationStackStore - 针对iOS 16:将视图包裹在中
WithPerceptionTracking { } - 运行现有测试 —— 所有测试通过后再处理下一个功能
- 完整迁移清单和语法转换规则可参考
migration.md
Code Generation Rules
代码生成规则
<critical_rules>
AI tools consistently generate outdated TCA code. Every output must use MODERN TCA (1.7+). ALWAYS:
- Use macro — never bare
@Reducerconformancestruct Feature: Reducer - Use on ALL State types — never generate State without it
@ObservableState - Define State AND Action INSIDE the struct body — never in extensions (macros can't see them)
@Reducer - Use for Destination/Path reducers (TCA 1.8+) — eliminates massive boilerplate
@Reducer enum - Action does NOT need — remove it. Why: TCA 1.4+ uses case key paths for cancellation and
Equatablematching, making Equatable conformance unnecessary and a maintenance burdenreceive() - Access store properties directly: ,
store.count— NEVER usestore.send(.tapped)WithViewStore - Use when
@Bindable var storebindings are needed$store - Use for effects — never
.run { send in },EffectTask,.task { }.fireAndForget { } - Use enum-with-cases for cancel IDs — never empty enum types (pruned in release builds)
- Never capture whole in effect closures — extract needed values first
@ObservableState - Never do expensive work in reducers — they run on main thread. Offload to effects. </critical_rules>
.run
<fallback_strategies>
When working with TCA code, you may encounter cryptic compiler errors. If you fail to fix the same error twice:
- "compiler is unable to type-check this expression": State/Action likely defined in extension instead of inside struct. Move them inside.
@Reducer - "Circular reference resolving attached macro 'Reducer'": Don't nest inside extension of another
@Reducer struct Y. Extract to top level.@Reducer struct X - Macro + property wrapper conflict: Avoid property wrappers inside State. Use
@ObservableStateas workaround (changes won't trigger re-renders).@ObservationStateIgnored - "A 'reduce' method should not be defined in a reducer with a 'body'": Never define BOTH AND
reduce(into:action:)in the same reducer.var body - NavigationStack dismiss fights: Ensure is OUTSIDE
.navigationDestination/ForEach, not inside. </fallback_strategies>List
<critical_rules>
AI工具经常生成过时的TCA代码,所有输出必须使用现代(1.7+)版本的TCA写法,始终遵循以下规则:
- 使用宏 —— 不要直接使用
@Reducer声明struct Feature: Reducer - 所有State类型都要添加—— 不要生成不带该注解的State
@ObservableState - State和Action都要定义在结构体内部 —— 不要定义在扩展中(宏无法识别扩展中的定义)
@Reducer - 目的地/路径reducer使用(TCA 1.8+支持) —— 可大幅减少样板代码
@Reducer enum - Action不需要遵循—— 移除相关声明。原因:TCA 1.4+使用case key path实现取消和
Equatable匹配,Equatable协议遵循没有必要,还会增加维护成本receive() - 直接访问store属性:、
store.count—— 绝对不要使用store.send(.tapped)WithViewStore - 需要使用绑定时声明
$store@Bindable var store - 副作用使用—— 不要使用
.run { send in }、EffectTask、.task { }.fireAndForget { } - 取消ID使用带case的枚举 —— 不要使用空枚举类型( release构建时会被裁剪)
- 不要在副作用闭包中捕获整个—— 先提取需要用到的值
@ObservableState - 不要在reducer中执行耗时操作 —— 它们运行在主线程,耗时逻辑要放到副作用中执行。 </critical_rules>
.run
<fallback_strategies>
处理TCA代码时可能会遇到晦涩的编译错误,如果同一个错误尝试两次都无法修复,可以参考以下方案:
- "compiler is unable to type-check this expression":State/Action大概率定义在扩展中而非结构体内部,将它们移到结构体内部即可。
@Reducer - "Circular reference resolving attached macro 'Reducer'":不要将嵌套在另一个
@Reducer struct Y的扩展中,将其提取到顶层即可。@Reducer struct X - 宏和属性包装器冲突:避免在修饰的State中使用属性包装器,可使用
@ObservableState作为临时解决方案(属性变更不会触发重渲染)。@ObservationStateIgnored - "A 'reduce' method should not be defined in a reducer with a 'body'":同一个reducer中不要同时定义和
reduce(into:action:)。var body - NavigationStack dismiss冲突:确保定义在
.navigationDestination/ForEach外部,而非内部。 </fallback_strategies>List
Confidence Checks
校验清单
Before finalizing generated or refactored TCA code, verify ALL:
[ ] @Reducer macro — present on all feature structs
[ ] @ObservableState — present on all State types
[ ] State/Action — defined INSIDE @Reducer struct, not in extensions
[ ] No WithViewStore — direct store access everywhere
[ ] No Equatable on Action — removed (unnecessary since TCA 1.4)
[ ] Delegate actions — child-parent communication uses .delegate pattern
[ ] Effect closures — capture only needed values, not whole state
[ ] Cancel IDs — enum with cases, NOT empty enum types
[ ] Navigation — @Presents for modals, StackState for push nav, never nested NavigationStack
[ ] Dependencies — @DependencyClient struct-of-closures, not protocols
[ ] Tests — TestStore with exhaustive assertions, @MainActor annotated
[ ] File size — new files <= 400 lines; oversized files have split task in refactoring/最终输出生成或重构后的TCA代码前,请确认所有检查项都已满足:
[ ] @Reducer macro — present on all feature structs
[ ] @ObservableState — present on all State types
[ ] State/Action — defined INSIDE @Reducer struct, not in extensions
[ ] No WithViewStore — direct store access everywhere
[ ] No Equatable on Action — removed (unnecessary since TCA 1.4)
[ ] Delegate actions — child-parent communication uses .delegate pattern
[ ] Effect closures — capture only needed values, not whole state
[ ] Cancel IDs — enum with cases, NOT empty enum types
[ ] Navigation — @Presents for modals, StackState for push nav, never nested NavigationStack
[ ] Dependencies — @DependencyClient struct-of-closures, not protocols
[ ] Tests — TestStore with exhaustive assertions, @MainActor annotated
[ ] File size — new files <= 400 lines; oversized files have split task in refactoring/Companion Skills
配套技能
| Project need | Companion skill | Apply when |
|---|---|---|
| Swift Concurrency patterns | | Writing async effects, actor isolation, Sendable compliance |
| GCD/OperationQueue legacy code | | Legacy async work before migrating to TCA effects |
| Comprehensive testing guidance | | Advanced testing patterns beyond TCA TestStore |
| Security audit | | Auditing Keychain usage, network security in TCA apps |
| 项目需求 | 配套技能 | 适用场景 |
|---|---|---|
| Swift 并发模式 | | 编写异步副作用、actor隔离、遵循Sendable协议 |
| GCD/OperationQueue 旧代码 | | 迁移到TCA副作用之前的旧异步逻辑处理 |
| 全面测试指南 | | TCA TestStore之外的高级测试模式 |
| 安全审计 | | 审计TCA应用的钥匙串使用、网络安全 |
References
参考文档
| Reference | When to Read |
|---|---|
| Do's and Don'ts quick reference: modern TCA patterns and critical anti-patterns |
| @Reducer macro rules, feature decomposition, parent-child scoping, state design |
| Effect API (.run, .send, .merge, .cancel), cancellation, long-running effects, anti-patterns |
| @DependencyClient, DependencyKey, liveValue/testValue, module boundaries, test guards |
| Tree-based (@Presents/ifLet), stack-based (StackState/forEach), dismissal, deep linking |
| TestStore exhaustive/non-exhaustive, TestClock, case key paths, per-feature checklist |
| Version progression, per-feature migration checklist, syntax transformations, known issues |
| AI-specific mistakes, god reducer signs, performance pitfalls, enterprise concerns |
| Action costs, _printChanges, .signpost, scope performance, high-frequency action mitigation |
| |
| 参考文档 | 阅读场景 |
|---|---|
| 最佳实践速查:现代TCA模式和核心反模式 |
| @Reducer宏规则、功能拆分、父子作用域、状态设计 |
| Effect API(.run、.send、.merge、.cancel)、取消机制、长驻副作用、反模式 |
| @DependencyClient、DependencyKey、liveValue/testValue、模块边界、测试防护 |
| 树结构导航(@Presents/ifLet)、栈结构导航(StackState/forEach)、关闭逻辑、深度链接 |
| TestStore 全覆盖/非全覆盖测试、TestClock、case key path、单功能检查清单 |
| 版本演进、单功能迁移清单、语法转换、已知问题 |
| AI特有的错误、上帝Reducer特征、性能陷阱、企业级注意事项 |
| Action开销、_printChanges、.signpost、作用域性能、高频Action优化 |
| |