本技能面向使用 The Composable Architecture(TCA,由 Point-Free 开发)构建 iOS/macOS 功能的开发场景,目标是帮助开发者按照 TCA 模式编写正确、可测试、可组合的 Swift 代码。
核心原则:单向数据流(Action → Reducer → State → View);State 使用值类型(简单、可等价的 struct);副作用显式化,Reducer 以 Effect 值的形式返回副作用;以组合代替继承,保持模块小而隔离、可重新组合;可测试优先,每个功能都应能用 TestStore 测试。
四大构件:State 承载 UI 与逻辑所需数据(@ObservableState struct);Action 表示全部事件,包括用户操作、effect 回调与 delegate(使用带 @CasePathable 的 enum);Reducer 是演进状态并返回 effect 的纯函数(@Reducer 宏);Store 是连接状态、reducer 与视图的运行时对象(StoreOf<Feature>)。
功能结构:文档给出了 @Reducer struct Feature 示例,State 中使用 IdentifiedArrayOf<Item> 与 isLoading,Action 中定义 onAppear、itemsResponse、delegate 及其 Delegate 子枚举,通过 @Dependency(.apiClient) 注入依赖,并在 body 中使用 Reduce 切换 action:onAppear 置 isLoading 为 true 并返回 .run 异步拉取,itemsResponse 的 success/failure 分别更新状态。
Store 与视图连接:在应用入口创建 store 并向下传递给视图,绝不在视图内部创建 store。示例中 FeatureView 持有 StoreOf<Feature>,在 List 中渲染 store.items,并在 onAppear 中发送 .onAppear。
Effects 模式:.none 用于同步状态变更且无副作用;.run { send in } 用于异步工作并回传 action;.cancellable(id:) 用于长时或可替换的 effect;.cancel(id:) 取消正在运行的 effect;.merge(...) 并行运行多个 effect;.concatenate(...) 顺序运行 effect。取消示例通过 .cancellable(id: CancelID.search, cancelInFlight: true) 自动取消同 ID 的先前 effect。
依赖:内置依赖包括 @Dependency(.uuid)、@Dependency(.date)、@Dependency(.continuousClock)、@Dependency(.mainQueue);自定义依赖的做法是定义带闭包的 client struct、实现 DependencyKey 的 liveValue/testValue/previewValue、扩展 DependencyValues 计算属性,并在 reducer 中使用 @Dependency(.yourClient),测试中通过 withDependencies 覆盖。
组合:子功能用 Scope 嵌入(Scope(state: .child, action: .child) { ChildFeature() }),视图侧用 store.scope;集合使用 IdentifiedArrayOf<ChildFeature.State> 配合 .forEach(.items, action: .items)。
导航:树型导航(sheet、alert、单层下钻)用可选状态 @Presents var detail、PresentationAction、.ifLet 与 .sheet(item:);栈型导航(NavigationStack、深链)用 StackState<Path.State> 与 StackActionOf<Path>,定义 @Reducer enum Path,配合 .forEach(.path, action: .path) 与 NavigationStack(path:)。
Delegate:子功能发出 delegate action 传递结果,父功能响应,而子功能无需知道父功能的实现。
测试:TestStore 以 initialState、reducer 与 withDependencies 构造,通过 store.send 断言状态变化、store.receive 接收 effect 结果。关键做法包括覆盖依赖(测试中不访问真实 API)、断言所有状态变更、接收所有 effect(TestStore 强制穷尽)、使用 TestClock 通过 clock.advance(by:) 控制基于时间的 effect、以及测试父子组合功能的集成测试。
高阶 Reducer:针对日志、分析、指标、功能开关等横切关注点,可通过扩展 Reducer 添加 .analytics 之类的方法。
现代 TCA(2025+):@Reducer 宏生成样板代码;@ObservableState 取代手写的 WithViewStore;@CasePathable 支持以 key path 语法书写 action;@Dependency 搭配内置 client(Clock、UUID、Date);SwiftUI 需要时在 State 上加 @MainActor;视图中直接访问 store,不再使用 viewStore。
必须遵守:保持 reducer 纯净,副作用只通过 Effect;集合使用 IdentifiedArray;测试状态迁移与 effect 输出;子功能到父功能的通信用 delegate。禁止:在 reducer 之外修改状态;在 reducer 中直接调用异步代码;在视图内部创建 store;用 @State/@StateObject 管理 TCA 状态;在测试中跳过接收 action。