Loading...
Loading...
Production-first enterprise skill for The Composable Architecture (TCA) with SwiftUI (iOS 16+, TCA 1.7+). This skill should be used when building new TCA features with @Reducer macro, decomposing god reducers, implementing StackState/StackAction navigation or tree-based @Presents navigation, writing TestStore tests, migrating legacy TCA code to modern @ObservableState patterns, debugging TCA performance issues, managing side effects and dependencies with @DependencyClient, or reviewing TCA code for anti-patterns. Use this skill any time someone works with TCA reducers, stores, effects, or dependencies — AI tools consistently generate outdated pre-1.7 TCA patterns, so this skill is essential for correct code.
npx skill4agent add rusel95/ios-agent-skills tca-swiftui-architectureApproach: 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/
@Reducer@ObservableState@PresentsStackStateWithViewStoreIfLetStoreEnvironmentIf the project uses TCA <1.7, consultfor pre-macro patterns and incremental upgrade path.references/migration.md
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.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 reducerWhat 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/alertsChild 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@ObservableState struct@ReducerEquatable@ReducerEquatableview(ViewAction)internal(InternalAction)delegate(DelegateAction)rules.mdvar body: some ReducerOf<Self>Reduce<State, Action> { }ScopeifLetforEachReduce@DependencyClientdependencies.mdstore.property@Bindable var store$testing.mdanti-patterns.mdrefactoring/refactoring-workflow.mdScope(state:action:).ifLet(\.$destination, action: \.destination).forEach(\.path, action: \.path)testing.md@Reducermigration.md@ObservableState@PresentationState@PresentsWithViewStore(store, observe: { $0 })store.propertyIfLetStoreForEachStoreSwitchStoreNavigationStackStoreNavigationStack(path: $store.scope(...))WithPerceptionTracking { }migration.md@Reducerstruct Feature: Reducer@ObservableState@Reducer@Reducer enumEquatablereceive()store.countstore.send(.tapped)WithViewStore@Bindable var store$store.run { send in }EffectTask.task { }.fireAndForget { }@ObservableState.run@Reducer@Reducer struct Y@Reducer struct X@ObservableState@ObservationStateIgnoredreduce(into:action:)var body.navigationDestinationForEachList[ ] @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/| 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 |
| 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 |
| |