Tca architect
Architect modular iOS apps using Swift Package Manager and The Composable Architecture (TCA). Use when designing or implementing a new iOS app (or feature module) that should be split into separate SPM packages, each owning a TCA feature reducer, view, and tests. Covers the full workflow: module decomposition, Package.swift dependency graph, reducer/view/navigation patterns, dependency injection via swift-dependencies, and the delegate action pattern for cross-module communication. Trigger when the user asks to: architect a TCA app, create a new SPM module/feature, set up a modular iOS project, add a feature to an existing TCA app, wire navigation between features, or design a dependency client.From its SKILL.md
npx -y skills add ninjaproger/skills --skill tca-architectAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
6.6 KB, ~1.4k tokens by cl100k_base, as published. Nobody here has run it
TCA + SPM Modular Architecture
Project Layout
MyApp/
├── MyApp/ # Xcode app target — THIN HOST ONLY (~16 lines)
│ └── MyApp.swift # @main — creates one Store, renders root view
└── MyAppKit/ # Single SPM package containing ALL code
├── Package.swift
├── Sources/
│ ├── AppCore/ # Root reducer + root view, composes all tab features
│ ├── DesignSystem/ # Tokens, colors, fonts — no TCA dependency
│ ├── Models/ # Pure Swift value types — no TCA, no UI
│ ├── Services/ # Dependency clients + actors — no UI
│ ├── <SharedComponent>/ # Reusable TCA reducer used by 2+ features (Quiz, ChapterStep…)
│ └── <Feature>/ # One module per tab/top-level feature
│ └── Resources/ # JSON, images owned by this module (.process("Resources"))
└── Tests/
└── <Feature>Tests/
Rules:
- The Xcode target never contains business logic. All code lives in the SPM package.
- No file header comments — files start directly with
importstatements. - Each module that owns static resources (
Resources/) must expose a public<Module>Bundle.swiftwithpublic static let bundle = Bundle.moduleso sibling modules can access those resources without a circular dependency.
Workflow
- Decompose features → Read
references/module-design.md - Set up Package.swift → Read
references/module-design.md(Package.swift section) - Implement reducers/views/navigation → Read
references/tca-patterns.md - Define dependency clients → Read
references/dependency-patterns.md - Wire root AppCore → See App Entry Point below
App Entry Point
// MyApp/MyAppApp.swift
import ComposableArchitecture
import AppCore
import SwiftUI
@main
struct MyApp: App {
let store = Store(initialState: AppCoreReducer.State()) {
AppCoreReducer()
}
var body: some Scene {
WindowGroup { AppCoreView(store: store) }
}
}
AppCore (Root Reducer + TabView)
AppCore imports every tab feature and composes them with Scope. The Reduce block at the end handles cross-feature logic by intercepting child delegate actions.
// Sources/AppCore/AppCoreView.swift
@Reducer
public struct AppCoreReducer {
public enum Tab: Hashable { case home, profile, settings }
@ObservableState
public struct State: Equatable {
var selectedTab: Tab = .home
var home = HomeReducer.State()
var profile = ProfileReducer.State()
var settings = SettingsReducer.State()
public init() {}
}
public enum Action {
case selectedTabChanged(Tab)
case home(HomeReducer.Action)
case profile(ProfileReducer.Action)
case settings(SettingsReducer.Action)
}
public var body: some ReducerOf<Self> {
Scope(state: \.home, action: \.home) { HomeReducer() }
Scope(state: \.profile, action: \.profile) { ProfileReducer() }
Scope(state: \.settings, action: \.settings) { SettingsReducer() }
Reduce { state, action in
switch action {
case .selectedTabChanged(let tab):
state.selectedTab = tab; return .none
case .settings(.delegate(.loggedOut)):
state.selectedTab = .home; return .none
case .home, .profile, .settings:
return .none
}
}
}
}
public struct AppCoreView: View {
@Bindable var store: StoreOf<AppCoreReducer>
public var body: some View {
TabView(selection: $store.selectedTab.sending(\.selectedTabChanged)) {
NavigationStack {
HomeView(store: store.scope(state: \.home, action: \.home))
}
.tabItem { Label("Home", systemImage: "house") }
.tag(AppCoreReducer.Tab.home)
// … other tabs
}
}
}
Key Principles
@ObservableStateon everyState— enables directstore.propertyreads in SwiftUI@Bindable var storein views — enables two-way$store.fieldbindingsScopebeforeReduce— child reducers always run before parent logic- Delegate actions for child→parent communication — see
references/tca-patterns.md @Reducer enum Destinationfor push/sheet/alert navigation — seereferences/tca-patterns.md- Dual
@Presentsfor overlay + navigation simultaneously — seereferences/tca-patterns.md @DependencyClientor manual struct for services — seereferences/dependency-patterns.md- Shared Component modules for reusable TCA reducers (used by 2+ features) — see
references/module-design.md - Bundle parameter when a child reducer loads resources from a parent module's
Resources/— seereferences/tca-patterns.md
Non-Negotiable Defaults
UI: Vanilla SwiftUI always. Use UIViewRepresentable only when SwiftUI has no equivalent (e.g. MKMapView, WKWebView, MTKView). Never wrap a UIKit component just for styling convenience.
Concurrency (in priority order):
async/await+AsyncStream+ Swift actors — always try this first- Combine — only if the API you're wrapping only exposes a
Publisherand no async equivalent exists - GCD /
NSLock/DispatchQueue— only as a last resort for legacy C/ObjC callback-only APIs
Reference Files
references/module-design.md— Module decomposition guide, Package.swift templates, dependency graph rules, shared bundle access patternreferences/tca-patterns.md— Full reducer pattern, navigation (Destination enum + @Presents), delegate actions, async effects, testingreferences/dependency-patterns.md—@DependencyClientmacro pattern, manual struct pattern, actor-as-dependency, live/test/mock implementations
What ships with it: 3 files
37.5 KB alongside SKILL.md
references/
- dependency-patterns.md11.2 KB
- module-design.md9.1 KB
- tca-patterns.md17.1 KB