Approach: 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 a refactoring/ directory. AI tools consistently generate outdated TCA code (pre-1.4 patterns, WithViewStore, Environment, Combine Effects). Every rule here exists to prevent those mistakes.
TCA SwiftUI Architecture (iOS 16+, TCA 1.7+)
Enterprise-grade skill for The Composable Architecture (point-free/swift-composable-architecture). Opinionated: prescribes @Reducer macro, @ObservableState, struct-of-closures dependencies, delegate actions for child-parent communication, and modern navigation via @Presents/StackState. TCA's API changed substantially across versions — AI tools trained on pre-1.7 code consistently generate WithViewStore, IfLetStore, Environment, and other removed patterns. This skill encodes the modern (1.7+) patterns validated against real enterprise codebases.
If the project uses TCA <1.7, consult references/migration.md for pre-macro patterns and incremental upgrade path.
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.
Quick Decision Trees
"Should this be its own 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 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/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 other
Workflows
Workflow: Create a New TCA Feature
When: Building a new screen or self-contained feature from scratch.
- Define State as
@ObservableState struct inside @Reducer — conform to Equatable
- Define Action enum inside
@Reducer — NO Equatable conformance needed (TCA 1.4+)
- Use three-category action pattern:
view(ViewAction), internal(InternalAction), delegate(DelegateAction) (rules.md)
- Implement
var body: some ReducerOf<Self> — use Reduce<State, Action> { } for explicit generics (helps Xcode autocomplete)
- Add child features via
Scope, ifLet, or forEach BEFORE parent Reduce block
- Create dependencies with
@DependencyClient macro (dependencies.md)
- Wire view: direct
store.property access, @Bindable var store for $ bindings
- Write exhaustive TestStore tests (
testing.md)
Workflow: Decompose a God 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
refactoring/ directory with per-feature plan files (refactoring-workflow.md)
- Extract child reducers bottom-up: leaf features first, then mid-level, then root
- Use
Scope(state:action:) for always-present children
- Use
.ifLet(\.$destination, action: \.destination) for optional children — effects auto-cancel on dismissal
- Use
.forEach(\.path, action: \.path) for stack navigation
- 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)
Workflow: Migrate Legacy TCA to Modern (1.7+)
When: Modernizing pre-1.7 TCA code that uses WithViewStore, IfLetStore, Environment, etc.
- Ensure TCA 1.4+ with
@Reducer macro on all reducers (prerequisite)
- Migrate feature-by-feature, bottom-up (leaf features first) (
migration.md)
- Per feature: add
@ObservableState to State AND update view simultaneously — never one without the other
- Replace
@PresentationState with @Presents
- Replace
WithViewStore(store, observe: { $0 }) with direct store.property access
- Replace
IfLetStore/ForEachStore/SwitchStore with native SwiftUI + store.scope
- Replace
NavigationStackStore with NavigationStack(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
Code Generation Rules
- Use
@Reducer macro — never bare struct Feature: Reducer conformance
- Use
@ObservableState on ALL State types — never generate State without it
- Define State AND Action INSIDE the
@Reducer struct body — never in extensions (macros can't see them)
- Use
@Reducer enum for Destination/Path reducers (TCA 1.8+) — eliminates massive boilerplate
- Action does NOT need
Equatable — remove it. Why: TCA 1.4+ uses case key paths for cancellation and receive() matching, making Equatable conformance unnecessary and a maintenance burden
- Access store properties directly:
store.count, store.send(.tapped) — NEVER use WithViewStore
- Use
@Bindable var store when $store bindings are needed
- Use
.run { send in } for effects — never EffectTask, .task { }, .fireAndForget { }
- Use enum-with-cases for cancel IDs — never empty enum types (pruned in release builds)
- Never capture whole
@ObservableState in effect closures — extract needed values first
- Never do expensive work in reducers — they run on main thread. Offload to
.run effects.
- "compiler is unable to type-check this expression": State/Action likely defined in extension instead of inside
@Reducer struct. Move them inside.
- "Circular reference resolving attached macro 'Reducer'": Don't nest
@Reducer struct Y inside extension of another @Reducer struct X. Extract to top level.
- Macro + property wrapper conflict: Avoid property wrappers inside
@ObservableState State. Use @ObservationStateIgnored as workaround (changes won't trigger re-renders).
- "A 'reduce' method should not be defined in a reducer with a 'body'": Never define BOTH
reduce(into:action:) AND var body in the same reducer.
- NavigationStack dismiss fights: Ensure
.navigationDestination is OUTSIDE ForEach/List, not inside.
Modernization Migration Cheat-Sheet
When migrating legacy TCA code or writing new features, these 7 tokens come up repeatedly. They are NOT optional — AI tools consistently default to the outdated form on the left. Always use the right column.
| Legacy / outdated |
Modern (TCA 1.7+, 2026) |
Why |
environment.mainQueue |
@Dependency(\.continuousClock) |
mainQueue was the pre-1.0 scheduler injection. In modern TCA, timing is a Clock dependency — override it with ImmediateClock or TestClock in tests. |
AnySchedulerOf<DispatchQueue> in State |
@Dependency(\.continuousClock) |
Same as above. Schedulers are no longer passed through State — they are dependencies. Use try await clock.sleep(for: .seconds(0.3)) in .run effects. |
@Bindable var store (iOS 17+ only) |
iOS 16: @Perception.Bindable var store — iOS 17+: @Bindable var store |
@Perception.Bindable is TCA's backport of SwiftUI's @Bindable for iOS 16 because Apple's @Bindable requires iOS 17. Without it, $store.binding fails to compile on iOS 16 targets. |
Bool showLogoutAlert in State |
@Presents var alert: AlertState<Action>? |
Alerts belong in AlertState, not in booleans — this gives you message text, button state, and action dispatching in one value, and it auto-dismisses through standard @Presents. |
[Product] in State |
IdentifiedArrayOf<Product> |
Plain arrays force O(n) updates-by-ID and confuse ForEachStore. IdentifiedArrayOf (import IdentifiedCollections) is the canonical TCA collection — constant-time lookup by id, stable animations, required by forEach(\.items, ...). |
var filtered: [T] { state.items.filter { ... } } (computed) |
Stored var filtered: IdentifiedArrayOf<T> updated in the reducer |
Computed vars on State hide reducer work from TCA's observation system and run on every access. Store the derived value and recompute it in the action that changes the source — it shows up in the action log and is testable. |
.run { try? await api.delete(id) } |
.run { send in try await api.delete(id); await send(.deleteSucceeded) } catch: { error, send in await send(.deleteFailed(error)) } |
try? inside effects is a silent-failure bug — the UI thinks the delete worked but nothing changed on the server. Use the catch: parameter on .run and emit an explicit failure action. |
Dismiss-in-delegate-handler pattern
When a child feature finishes (save, cancel, delete) and the parent uses @Presents, the parent MUST dismiss in the same handler that processes the delegate action:
case .destination(.presented(.editor(.delegate(.saved(let item))))):
state.items.append(item)
state.destination = nil // ← REQUIRED. Without this, the sheet stays open.
return .none
Forgetting state.destination = nil is the #1 TCA navigation bug — the save succeeds but the sheet never dismisses. For an alternative, child features can use @Dependency(\.dismiss) and call await dismiss() inside their own effects.
unimplemented(placeholder:) for non-Void test dependencies
@DependencyClient's testValue auto-generates unimplemented(...) for () -> Void closures, but returning non-Void requires a placeholder. Without it, the test crashes with "dependency has no implementation":
@DependencyClient
struct ProfileClient {
var fetch: @Sendable (User.ID) async throws -> Profile = {
_ in unimplemented("ProfileClient.fetch", placeholder: Profile.mock)
}
}
The placeholder: value is used only when the test does NOT assert against fetch — it prevents the crash while still failing the test if the call is unexpected.
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
[ ] Schedulers — `@Dependency(\.continuousClock)`, NOT `mainQueue` or `AnySchedulerOf`
[ ] iOS 16 bindings — `@Perception.Bindable` used instead of `@Bindable` when deployment target is iOS 16
[ ] Collections in State — `IdentifiedArrayOf` used instead of `[T]`
[ ] Derived state — stored property updated in reducer, NOT a computed var on State
[ ] `.run` effects — use `catch:` parameter, never `try?` inside effect closures
[ ] Alert state — `AlertState<Action>` in `@Presents`, NOT a `Bool showXAlert`
[ ] File size — new files <= 400 lines; oversized files have split task in refactoring/
Companion Skills
| Project need |
Companion skill |
Apply when |
| Swift Concurrency patterns |
skills/swift-concurrency/SKILL.md |
Writing async effects, actor isolation, Sendable compliance |
| GCD/OperationQueue legacy code |
skills/gcd-operations/SKILL.md |
Legacy async work before migrating to TCA effects |
| Comprehensive testing guidance |
skills/ios-testing/SKILL.md |
Advanced testing patterns beyond TCA TestStore |
| Security audit |
skills/ios-security/SKILL.md |
Auditing Keychain usage, network security in TCA apps |
References
| Reference |
When to Read |
references/rules.md |
Do's and Don'ts quick reference: modern TCA patterns and critical anti-patterns |
references/reducer-architecture.md |
@Reducer macro rules, feature decomposition, parent-child scoping, state design |
references/effects.md |
Effect API (.run, .send, .merge, .cancel), cancellation, long-running effects, anti-patterns |
references/dependencies.md |
@DependencyClient, DependencyKey, liveValue/testValue, module boundaries, test guards |
references/navigation.md |
Tree-based (@Presents/ifLet), stack-based (StackState/forEach), dismissal, deep linking |
references/testing.md |
TestStore exhaustive/non-exhaustive, TestClock, case key paths, per-feature checklist |
references/migration.md |
Version progression, per-feature migration checklist, syntax transformations, known issues |
references/anti-patterns.md |
AI-specific mistakes, god reducer signs, performance pitfalls, enterprise concerns |
references/performance.md |
Action costs, _printChanges, .signpost, scope performance, high-frequency action mitigation |
references/refactoring-workflow.md |
refactoring/ directory protocol, per-feature plans, PR sizing, phase ordering |
1---2name: tca-swiftui3description: Use for any question about The Composable Architecture (TCA) — defining reducers, managing state, handling navigation, writing tests, or working with dependencies. Essential when encountering TCA-specific types like @Reducer, @ObservableState, StackState, StackAction, @Presents, TestStore, @DependencyClient, Scope, or delegate actions. Also use when debugging TCA compiler errors, decomposing large reducers into child features, migrating from old TCA patterns (WithViewStore, Environment, IfLetStore), or implementing sheet/push navigation in TCA. Claude's training data contains outdated TCA patterns — invoke this skill whenever TCA code is involved to get correct modern (1.7+) patterns.4---56> **Approach: 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 a `refactoring/` directory. AI tools consistently generate outdated TCA code (pre-1.4 patterns, WithViewStore, Environment, Combine Effects). Every rule here exists to prevent those mistakes.78# TCA SwiftUI Architecture (iOS 16+, TCA 1.7+)910Enterprise-grade skill for The Composable Architecture (point-free/swift-composable-architecture). Opinionated: prescribes `@Reducer` macro, `@ObservableState`, struct-of-closures dependencies, delegate actions for child-parent communication, and modern navigation via `@Presents`/`StackState`. TCA's API changed substantially across versions — AI tools trained on pre-1.7 code consistently generate `WithViewStore`, `IfLetStore`, `Environment`, and other removed patterns. This skill encodes the modern (1.7+) patterns validated against real enterprise codebases.1112> **If the project uses TCA <1.7**, consult `references/migration.md` for pre-macro patterns and incremental upgrade path.1314## Architecture Layers1516```17View Layer → SwiftUI Views. Direct store access, NO WithViewStore.18 Owns store via let/var. Uses @Bindable for $ bindings.19Reducer Layer → @Reducer struct. State + Action + body. Pure logic.20 Returns Effect for async work. ALWAYS runs on main thread.21Effect Layer → .run { send in } for async. .send for sync delegate actions.22 Cancellable, mergeable, concatenatable.23Dependency Layer → @DependencyClient structs. Struct-of-closures, NOT protocols.24 liveValue (app) / testValue (auto-unimplemented) / previewValue.25```2627## Quick Decision Trees2829### "Should this be its own Feature reducer?"3031```32Does this component have its own screen/view?33├── YES → Own @Reducer struct34└── NO → Does it have business logic you want to unit test?35 ├── YES → Own @Reducer struct, compose via Scope36 └── NO → Is its state optional (not always visible)?37 ├── YES → Own @Reducer, use ifLet (avoids unnecessary work)38 └── NO → Keep logic in parent reducer39```4041### "What navigation pattern do I need?"4243```44What type of navigation?45├── Modal (sheet/alert/dialog/fullScreenCover)?46│ → Tree-based: @Presents + PresentationAction + ifLet47├── Single drill-down?48│ → Tree-based: optional state + navigationDestination49├── Multi-level push (NavigationStack)?50│ → Stack-based: StackState + StackAction + forEach51├── Deep linking is critical?52│ → Stack-based (easy to construct state array)53└── Modals FROM within a NavigationStack?54 → BOTH: stack for push nav, tree for sheets/alerts55```5657### "How should child communicate with parent?"5859```60Child needs to tell parent something happened?61├── Use delegate action: case delegate(DelegateAction)62│ Parent observes ONLY .delegate cases, never child internals63│64Child needs to share logic between action cases?65├── Use mutating func on State or private helper method66│ NEVER send an action to share logic — it traverses the entire reducer tree unnecessarily,67│ creating hidden coupling and making performance profiling difficult68│69Sibling reducers need to communicate?70└── Delegate up to parent, parent coordinates71 NEVER send actions between siblings directly — siblings should not know about each other72```7374## Workflows7576### Workflow: Create a New TCA Feature7778**When:** Building a new screen or self-contained feature from scratch.79801. Define State as `@ObservableState struct` inside `@Reducer` — conform to `Equatable`812. Define Action enum inside `@Reducer` — NO `Equatable` conformance needed (TCA 1.4+)823. Use three-category action pattern: `view(ViewAction)`, `internal(InternalAction)`, `delegate(DelegateAction)` (`rules.md`)834. Implement `var body: some ReducerOf<Self>` — use `Reduce<State, Action> { }` for explicit generics (helps Xcode autocomplete)845. Add child features via `Scope`, `ifLet`, or `forEach` BEFORE parent `Reduce` block856. Create dependencies with `@DependencyClient` macro (`dependencies.md`)867. Wire view: direct `store.property` access, `@Bindable var store` for `$` bindings878. Write exhaustive TestStore tests (`testing.md`)8889### Workflow: Decompose a God Reducer9091**When:** A single reducer handles logic for multiple screens, has 800+ lines, or testing is impossible to isolate.92931. Identify feature boundaries — each screen = own reducer942. Scan for anti-patterns using detection checklist (`anti-patterns.md`)953. Create `refactoring/` directory with per-feature plan files (`refactoring-workflow.md`)964. Extract child reducers bottom-up: leaf features first, then mid-level, then root975. Use `Scope(state:action:)` for always-present children986. Use `.ifLet(\.$destination, action: \.destination)` for optional children — effects auto-cancel on dismissal997. Use `.forEach(\.path, action: \.path)` for stack navigation1008. Convert child-parent communication to delegate actions — parent must NEVER observe child internal actions1019. Write non-exhaustive integration tests for cross-feature flows (`testing.md`)102103### Workflow: Migrate Legacy TCA to Modern (1.7+)104105**When:** Modernizing pre-1.7 TCA code that uses WithViewStore, IfLetStore, Environment, etc.1061071. Ensure TCA 1.4+ with `@Reducer` macro on all reducers (prerequisite)1082. Migrate feature-by-feature, bottom-up (leaf features first) (`migration.md`)1093. Per feature: add `@ObservableState` to State AND update view simultaneously — never one without the other1104. Replace `@PresentationState` with `@Presents`1115. Replace `WithViewStore(store, observe: { $0 })` with direct `store.property` access1126. Replace `IfLetStore`/`ForEachStore`/`SwitchStore` with native SwiftUI + store.scope1137. Replace `NavigationStackStore` with `NavigationStack(path: $store.scope(...))`1148. For iOS 16: wrap views in `WithPerceptionTracking { }`1159. Run existing tests — all must pass before proceeding to next feature11610. See full migration checklist and syntax transformations in `migration.md`117118## Code Generation Rules119120<critical_rules>121AI tools consistently generate outdated TCA code. Every output must use MODERN TCA (1.7+). ALWAYS:1221231. Use `@Reducer` macro — never bare `struct Feature: Reducer` conformance1242. Use `@ObservableState` on ALL State types — never generate State without it1253. Define State AND Action INSIDE the `@Reducer` struct body — never in extensions (macros can't see them)1264. Use `@Reducer enum` for Destination/Path reducers (TCA 1.8+) — eliminates massive boilerplate1275. Action does NOT need `Equatable` — remove it. Why: TCA 1.4+ uses case key paths for cancellation and `receive()` matching, making Equatable conformance unnecessary and a maintenance burden1286. Access store properties directly: `store.count`, `store.send(.tapped)` — NEVER use `WithViewStore`1297. Use `@Bindable var store` when `$store` bindings are needed1308. Use `.run { send in }` for effects — never `EffectTask`, `.task { }`, `.fireAndForget { }`1319. Use enum-with-cases for cancel IDs — never empty enum types (pruned in release builds)13210. Never capture whole `@ObservableState` in effect closures — extract needed values first13311. Never do expensive work in reducers — they run on main thread. Offload to `.run` effects.134</critical_rules>135136<fallback_strategies>137When working with TCA code, you may encounter cryptic compiler errors. If you fail to fix the same error twice:1381391. **"compiler is unable to type-check this expression"**: State/Action likely defined in extension instead of inside `@Reducer` struct. Move them inside.1402. **"Circular reference resolving attached macro 'Reducer'"**: Don't nest `@Reducer struct Y` inside extension of another `@Reducer struct X`. Extract to top level.1413. **Macro + property wrapper conflict**: Avoid property wrappers inside `@ObservableState` State. Use `@ObservationStateIgnored` as workaround (changes won't trigger re-renders).1424. **"A 'reduce' method should not be defined in a reducer with a 'body'"**: Never define BOTH `reduce(into:action:)` AND `var body` in the same reducer.1435. **NavigationStack dismiss fights**: Ensure `.navigationDestination` is OUTSIDE `ForEach`/`List`, not inside.144</fallback_strategies>145146## Modernization Migration Cheat-Sheet147148When migrating legacy TCA code or writing new features, these 7 tokens come up repeatedly. They are NOT optional — AI tools consistently default to the outdated form on the left. Always use the right column.149150| Legacy / outdated | Modern (TCA 1.7+, 2026) | Why |151|---|---|---|152| `environment.mainQueue` | `@Dependency(\.continuousClock)` | `mainQueue` was the pre-1.0 scheduler injection. In modern TCA, timing is a `Clock` dependency — override it with `ImmediateClock` or `TestClock` in tests. |153| `AnySchedulerOf<DispatchQueue>` in State | `@Dependency(\.continuousClock)` | Same as above. Schedulers are no longer passed through State — they are dependencies. Use `try await clock.sleep(for: .seconds(0.3))` in `.run` effects. |154| `@Bindable var store` (iOS 17+ only) | **iOS 16**: `@Perception.Bindable var store` — **iOS 17+**: `@Bindable var store` | `@Perception.Bindable` is TCA's backport of SwiftUI's `@Bindable` for iOS 16 because Apple's `@Bindable` requires iOS 17. Without it, `$store.binding` fails to compile on iOS 16 targets. |155| `Bool showLogoutAlert` in State | `@Presents var alert: AlertState<Action>?` | Alerts belong in `AlertState`, not in booleans — this gives you message text, button state, and action dispatching in one value, and it auto-dismisses through standard `@Presents`. |156| `[Product]` in State | `IdentifiedArrayOf<Product>` | Plain arrays force O(n) updates-by-ID and confuse `ForEachStore`. `IdentifiedArrayOf` (`import IdentifiedCollections`) is the canonical TCA collection — constant-time lookup by `id`, stable animations, required by `forEach(\.items, ...)`. |157| `var filtered: [T] { state.items.filter { ... } }` (computed) | Stored `var filtered: IdentifiedArrayOf<T>` updated in the reducer | Computed vars on State hide reducer work from TCA's observation system and run on every access. Store the derived value and recompute it in the action that changes the source — it shows up in the action log and is testable. |158| `.run { try? await api.delete(id) }` | `.run { send in try await api.delete(id); await send(.deleteSucceeded) } catch: { error, send in await send(.deleteFailed(error)) }` | `try?` inside effects is a silent-failure bug — the UI thinks the delete worked but nothing changed on the server. Use the `catch:` parameter on `.run` and emit an explicit failure action. |159160### Dismiss-in-delegate-handler pattern161162When a child feature finishes (save, cancel, delete) and the parent uses `@Presents`, the parent MUST dismiss in the same handler that processes the delegate action:163164```swift165case .destination(.presented(.editor(.delegate(.saved(let item))))):166 state.items.append(item)167 state.destination = nil // ← REQUIRED. Without this, the sheet stays open.168 return .none169```170171Forgetting `state.destination = nil` is the #1 TCA navigation bug — the save succeeds but the sheet never dismisses. For an alternative, child features can use `@Dependency(\.dismiss)` and call `await dismiss()` inside their own effects.172173### `unimplemented(placeholder:)` for non-Void test dependencies174175`@DependencyClient`'s `testValue` auto-generates `unimplemented(...)` for `() -> Void` closures, but returning non-Void requires a placeholder. Without it, the test crashes with "dependency has no implementation":176177```swift178@DependencyClient179struct ProfileClient {180 var fetch: @Sendable (User.ID) async throws -> Profile = {181 _ in unimplemented("ProfileClient.fetch", placeholder: Profile.mock)182 }183}184```185186The `placeholder:` value is used only when the test does NOT assert against `fetch` — it prevents the crash while still failing the test if the call is unexpected.187188## Confidence Checks189190Before finalizing generated or refactored TCA code, verify ALL:191192```193[ ] @Reducer macro — present on all feature structs194[ ] @ObservableState — present on all State types195[ ] State/Action — defined INSIDE @Reducer struct, not in extensions196[ ] No WithViewStore — direct store access everywhere197[ ] No Equatable on Action — removed (unnecessary since TCA 1.4)198[ ] Delegate actions — child-parent communication uses .delegate pattern199[ ] Effect closures — capture only needed values, not whole state200[ ] Cancel IDs — enum with cases, NOT empty enum types201[ ] Navigation — @Presents for modals, StackState for push nav, never nested NavigationStack202[ ] Dependencies — @DependencyClient struct-of-closures, not protocols203[ ] Tests — TestStore with exhaustive assertions, @MainActor annotated204[ ] Schedulers — `@Dependency(\.continuousClock)`, NOT `mainQueue` or `AnySchedulerOf`205[ ] iOS 16 bindings — `@Perception.Bindable` used instead of `@Bindable` when deployment target is iOS 16206[ ] Collections in State — `IdentifiedArrayOf` used instead of `[T]`207[ ] Derived state — stored property updated in reducer, NOT a computed var on State208[ ] `.run` effects — use `catch:` parameter, never `try?` inside effect closures209[ ] Alert state — `AlertState<Action>` in `@Presents`, NOT a `Bool showXAlert`210[ ] File size — new files <= 400 lines; oversized files have split task in refactoring/211```212213## Companion Skills214215| Project need | Companion skill | Apply when |216|---|---|---|217| Swift Concurrency patterns | `skills/swift-concurrency/SKILL.md` | Writing async effects, actor isolation, Sendable compliance |218| GCD/OperationQueue legacy code | `skills/gcd-operations/SKILL.md` | Legacy async work before migrating to TCA effects |219| Comprehensive testing guidance | `skills/ios-testing/SKILL.md` | Advanced testing patterns beyond TCA TestStore |220| Security audit | `skills/ios-security/SKILL.md` | Auditing Keychain usage, network security in TCA apps |221222## References223224| Reference | When to Read |225|-----------|-------------|226| `references/rules.md` | Do's and Don'ts quick reference: modern TCA patterns and critical anti-patterns |227| `references/reducer-architecture.md` | @Reducer macro rules, feature decomposition, parent-child scoping, state design |228| `references/effects.md` | Effect API (.run, .send, .merge, .cancel), cancellation, long-running effects, anti-patterns |229| `references/dependencies.md` | @DependencyClient, DependencyKey, liveValue/testValue, module boundaries, test guards |230| `references/navigation.md` | Tree-based (@Presents/ifLet), stack-based (StackState/forEach), dismissal, deep linking |231| `references/testing.md` | TestStore exhaustive/non-exhaustive, TestClock, case key paths, per-feature checklist |232| `references/migration.md` | Version progression, per-feature migration checklist, syntax transformations, known issues |233| `references/anti-patterns.md` | AI-specific mistakes, god reducer signs, performance pitfalls, enterprise concerns |234| `references/performance.md` | Action costs, _printChanges, .signpost, scope performance, high-frequency action mitigation |235| `references/refactoring-workflow.md` | `refactoring/` directory protocol, per-feature plans, PR sizing, phase ordering |