agentsclimarketplace

Tca architect

Skill ninjaproger/skills/skills/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

Install
npx -y skills add ninjaproger/skills --skill tca-architect

Assembled 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 import statements.
  • Each module that owns static resources (Resources/) must expose a public <Module>Bundle.swift with public static let bundle = Bundle.module so sibling modules can access those resources without a circular dependency.

Workflow

  1. Decompose features → Read references/module-design.md
  2. Set up Package.swift → Read references/module-design.md (Package.swift section)
  3. Implement reducers/views/navigation → Read references/tca-patterns.md
  4. Define dependency clients → Read references/dependency-patterns.md
  5. 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

  • @ObservableState on every State — enables direct store.property reads in SwiftUI
  • @Bindable var store in views — enables two-way $store.field bindings
  • Scope before Reduce — child reducers always run before parent logic
  • Delegate actions for child→parent communication — see references/tca-patterns.md
  • @Reducer enum Destination for push/sheet/alert navigation — see references/tca-patterns.md
  • Dual @Presents for overlay + navigation simultaneously — see references/tca-patterns.md
  • @DependencyClient or manual struct for services — see references/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/ — see references/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):

  1. async/await + AsyncStream + Swift actors — always try this first
  2. Combine — only if the API you're wrapping only exposes a Publisher and no async equivalent exists
  3. 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 pattern
  • references/tca-patterns.md — Full reducer pattern, navigation (Destination enum + @Presents), delegate actions, async effects, testing
  • references/dependency-patterns.md@DependencyClient macro pattern, manual struct pattern, actor-as-dependency, live/test/mock implementations

What ships with it: 3 files

37.5 KB alongside SKILL.md

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.