ios-architecture-expert
Installation
SKILL.md
iOS Architecture Expert — Clean Modular Architecture
Agent Behavior Contract
When this skill is active, follow these rules strictly:
- Feature modules have zero UIKit/SwiftUI imports — only Foundation. Domain models, use cases, presenters, and API/cache logic never depend on a UI framework.
- Define a boundary at every layer transition — protocol or closure. A layer never references a concrete type from another layer. Use a protocol when there are genuinely multiple strategies (
FeedStore,HTTPClient,ResourceView,FeedCache); use a plain composed closure (() async throws -> Resource) when there is one — see rule 12. - Domain models are value types —
struct,Hashable,Sendable. No classes for models. - Presentation logic is framework-agnostic — presenters output view models (structs). No
UIImage,UIColor, or SwiftUI types in the presentation layer. - All dependency wiring happens in the Composition Root — the app target (or a dedicated
CompositionRootmodule). Feature modules never create their own dependencies. - Tests drive the design — one test class per use case, named by behavior (
CacheFeedUseCaseTests, notLocalFeedLoaderTests). UsemakeSUT()factory in every test class. - Test only the public API — no
@testable importoutside the Composition Root. Test targets use a plainimport Feed; a behavior that can't be observed publicly is a missing public seam (a boundary, a return value, an injected collaborator), never a reason to widen the test's access. This is deliberately stricter than the source codebase, which uses@testablein a handful of places — the full argument and the Composition Root exception live intestability-and-seams.md. - Use SPM multi-target packages for module separation —
Feed(Foundation),FeediOS(UIKit),App(composition). - Prefer async/await over closures/Combine for async operations. Use
Task.immediatefor synchronous-first execution in adapters where the deployment target allows it (iOS 26+; plainTask+ observable-state tests is the documented fallback — seeconcurrency-at-boundaries.md). - Match the project's existing test framework; default to Swift Testing for greenfield suites. For test doubles, test design, deterministic async testing, Swift Testing syntax and XCTest migration, defer to the
swift-testing-expertskill. This skill keeps only the places where a testing decision is a design decision — seetestability-and-seams.md. Two things that bite regardless:trackForMemoryLeaks/addTeardownBlockis XCTest-only (the Swift Testing equivalent is a test-scoping trait, ST-0007), and Swift Testing runs tests in parallel by default, so suites sharing a store URL or on-disk artifacts need.serialized. - Mark shared mutable state with
@MainActor— presenters, adapters, view controllers, and composition code run on the main actor. - Do not add a protocol boundary that has only one implementation and no second strategy in sight — compose a closure in the Composition Root instead. Boundaries exist to select between strategies.