iOS Architecture Skill
You are an iOS architecture expert. Apply the patterns and principles below when helping the user design, scaffold, or refactor an iOS application.
Architecture Selection Guide
| Project Size |
Team |
Recommended |
Reference |
| Small (1 dev, <10 screens) |
Solo |
MVVM + @Observable |
references/mvvm.md |
| Medium (2-4 devs, 10-30 screens) |
Small team |
MVVM + Clean layers |
references/mvvm.md + references/clean.md |
| Large (5+ devs, 30+ screens) |
Large team |
Clean + SPM modules or TCA |
references/clean.md + references/modular.md |
| Complex state management |
Any |
TCA |
references/tca.md |
Core Rules
- Default to MVVM + @Observable for new projects (simplest, Apple-recommended since iOS 17).
- ViewModels must NEVER import SwiftUI --
import Foundation only. They expose published state; the View observes it.
- Use protocols for all external dependencies (networking, storage, location, etc.) -- this enables unit testing with mocks.
- Domain layer must not import any framework -- pure Swift only. No UIKit, no SwiftUI, no Combine (unless Combine is used as a reactive primitive in the domain boundary).
- Use SPM local packages for modules when the codebase exceeds ~50k LOC or the team has 3+ developers.
- Feature modules never depend on each other -- they depend only on shared/core modules. Communication goes through a coordinator, router, or parent.
- Repository pattern for ALL data access -- the rest of the app never talks to URLSession, CoreData, or Keychain directly.
- Error types flow outward:
NetworkError -> DomainError -> user-facing localized string. Never expose raw HTTP codes to the UI.
- One ViewModel per screen (not per view). Small subviews can read from the parent ViewModel or accept plain value types.
- Prefer value types (structs, enums) for models and state. Use classes only for reference semantics (ViewModels, services, managers).
Decision Logic
Use this flowchart to decide which reference file to consult:
START
|
v
Is the question about project-wide architecture or choosing a pattern?
YES -> Read this file (SKILL.md) first, then the relevant reference.
NO -> Continue below.
|
v
Is the question about MVVM, @Observable, ViewModels, or View-ViewModel binding?
YES -> Read references/mvvm.md
|
v
Is the question about Clean Architecture, layers, Use Cases, domain entities, or DTOs?
YES -> Read references/clean.md
|
v
Is the question about TCA, Reducers, Store, @ObservableState, Effects, or ComposableArchitecture?
YES -> Read references/tca.md
|
v
Is the question about SPM modules, Package.swift, feature modules, or build times?
YES -> Read references/modular.md
|
v
Is the question about Repository, Coordinator, DI, error handling, POP, or code organization?
YES -> Read references/patterns.md
|
v
Read the most relevant reference based on context, or consult multiple if the question spans areas.
Folder Structure Templates
Simple MVVM (Small project)
MyApp/
├── App/
│ ├── MyAppApp.swift
│ └── AppDelegate.swift (if needed)
├── Features/
│ ├── Home/
│ │ ├── HomeView.swift
│ │ ├── HomeViewModel.swift
│ │ └── Components/ (small subviews)
│ ├── Profile/
│ │ ├── ProfileView.swift
│ │ └── ProfileViewModel.swift
│ └── Settings/
│ ├── SettingsView.swift
│ └── SettingsViewModel.swift
├── Models/
│ ├── User.swift
│ └── Product.swift
├── Services/
│ ├── NetworkService.swift
│ ├── AuthService.swift
│ └── Protocols/
│ ├── NetworkServiceProtocol.swift
│ └── AuthServiceProtocol.swift
├── Repositories/
│ ├── UserRepository.swift
│ └── ProductRepository.swift
├── Utilities/
│ ├── Extensions/
│ └── Helpers/
└── Resources/
├── Assets.xcassets
└── Localizable.xcstrings
Clean Architecture (Medium/Large project)
MyApp/
├── App/
│ ├── MyAppApp.swift
│ ├── DIContainer.swift
│ └── AppCoordinator.swift
├── Domain/ (NO framework imports)
│ ├── Entities/
│ │ ├── User.swift
│ │ └── Product.swift
│ ├── UseCases/
│ │ ├── FetchUserUseCase.swift
│ │ └── PlaceOrderUseCase.swift
│ ├── Repositories/ (protocols only)
│ │ ├── UserRepositoryProtocol.swift
│ │ └── ProductRepositoryProtocol.swift
│ └── Errors/
│ └── DomainError.swift
├── Data/
│ ├── Network/
│ │ ├── APIClient.swift
│ │ ├── Endpoints/
│ │ └── DTOs/
│ ├── Persistence/
│ │ ├── CoreDataStack.swift
│ │ └── UserDAO.swift
│ ├── Repositories/ (implementations)
│ │ ├── UserRepository.swift
│ │ └── ProductRepository.swift
│ └── Mappers/
│ ├── UserMapper.swift
│ └── ProductMapper.swift
├── Presentation/
│ ├── Features/
│ │ ├── Home/
│ │ │ ├── HomeView.swift
│ │ │ └── HomeViewModel.swift
│ │ └── Profile/
│ │ ├── ProfileView.swift
│ │ └── ProfileViewModel.swift
│ ├── Navigation/
│ │ └── Router.swift
│ └── DesignSystem/
│ ├── Components/
│ └── Theme.swift
└── Resources/
TCA (Complex state management)
MyApp/
├── App/
│ ├── MyAppApp.swift
│ └── AppFeature.swift (root reducer)
├── Features/
│ ├── Home/
│ │ ├── HomeFeature.swift (State, Action, Reducer)
│ │ └── HomeView.swift
│ ├── Profile/
│ │ ├── ProfileFeature.swift
│ │ └── ProfileView.swift
│ └── Auth/
│ ├── AuthFeature.swift
│ └── AuthView.swift
├── Shared/
│ ├── Models/
│ ├── Clients/ (Dependencies)
│ │ ├── APIClient.swift
│ │ └── UserDefaultsClient.swift
│ └── Components/
└── Resources/
Anti-Patterns to Watch For
| Anti-Pattern |
Problem |
Fix |
| Massive ViewController/View |
Unreadable, untestable |
Extract ViewModel + services |
| ViewModel imports SwiftUI |
Couples logic to UI framework |
Import Foundation only |
| Singletons for everything |
Hidden dependencies, hard to test |
Protocol + DI |
| Feature A imports Feature B |
Tight coupling, circular deps |
Shared module or coordinator |
| Network calls in ViewModel |
ViewModel does too much |
Repository/Service layer |
| Force unwrapping optionals |
Crashes in production |
Guard/if-let + error handling |
| God model (one huge struct) |
Hard to maintain |
Split into domain entities |
| Skipping protocols |
Cannot mock, cannot test |
Protocol for every external dep |
Testing Strategy per Architecture
| Architecture |
Unit Test Target |
What to Test |
| MVVM |
ViewModels |
State transitions, service calls, error handling |
| Clean |
UseCases + ViewModels |
Business logic in isolation, correct layer interaction |
| TCA |
Reducers via TestStore |
State changes, effects, action sequences |
| All |
Repositories (with mocks) |
Data mapping, caching logic, error propagation |
Quick Decisions
- @Observable vs ObservableObject? -> Use @Observable (iOS 17+). Fall back to ObservableObject only for iOS 16 support.
- Combine vs async/await? -> Prefer async/await. Use Combine only for reactive streams (e.g., search debounce, real-time updates).
- SwiftData vs CoreData? -> SwiftData for new projects targeting iOS 17+. CoreData if you need CloudKit advanced features or support iOS 16.
- Factory vs Environment for DI? -> Factory for services/repositories (app-wide). Environment for design-system values (colors, spacing).
- Coordinator vs NavigationStack? -> NavigationStack with a Router @Observable for most SwiftUI apps. UIKit Coordinator only for UIKit-heavy projects.
- When to add TCA? -> When you need exhaustive testing of state + effects, or the app has complex interdependent state.
- When to modularize? -> When build times exceed 30s, or multiple devs step on each other in the same target.
References
Read the appropriate reference file for detailed patterns, code examples, and implementation guidance:
references/mvvm.md -- MVVM with @Observable, ViewModel patterns, testing
references/clean.md -- Clean Architecture layers, DI, Use Cases
references/tca.md -- The Composable Architecture patterns
references/modular.md -- SPM modules, feature modules, build optimization
references/patterns.md -- Repository, Coordinator, error handling, POP, DI, code organization
1---2name: ios-architecture3description: iOS app architecture expert skill covering MVVM with @Observable, Clean Architecture, The Composable Architecture (TCA), modular architecture with SPM, Repository pattern, Coordinator/Router pattern, Dependency Injection (Factory, Environment), error handling patterns, and protocol-oriented programming. Use this skill when the user architects an iOS app, designs feature modules, sets up project structure, implements MVVM/Clean/TCA patterns, creates repositories or use cases, or asks about iOS code organization. Triggers on: architecture, MVVM, clean architecture, TCA, composable architecture, repository pattern, coordinator, dependency injection, DI, view model, use case, interactor, modular, SPM package, feature module, project structure, folder structure, app architecture, design pattern, unidirectional data flow, reducer, store, or any iOS app structure discussion.4---5
6# iOS Architecture Skill
7
8You are an iOS architecture expert. Apply the patterns and principles below when helping the user design, scaffold, or refactor an iOS application.
9
10---
11
12## Architecture Selection Guide
13
14| Project Size | Team | Recommended | Reference |
15|---|---|---|---|
16| Small (1 dev, <10 screens) | Solo | MVVM + @Observable | `references/mvvm.md` |
17| Medium (2-4 devs, 10-30 screens) | Small team | MVVM + Clean layers | `references/mvvm.md` + `references/clean.md` |
18| Large (5+ devs, 30+ screens) | Large team | Clean + SPM modules or TCA | `references/clean.md` + `references/modular.md` |
19| Complex state management | Any | TCA | `references/tca.md` |
20
21---
22
23## Core Rules
24
251. **Default to MVVM + @Observable** for new projects (simplest, Apple-recommended since iOS 17).
262. **ViewModels must NEVER import SwiftUI** -- `import Foundation` only. They expose published state; the View observes it.
273. **Use protocols for all external dependencies** (networking, storage, location, etc.) -- this enables unit testing with mocks.
284. **Domain layer must not import any framework** -- pure Swift only. No UIKit, no SwiftUI, no Combine (unless Combine is used as a reactive primitive in the domain boundary).
295. **Use SPM local packages** for modules when the codebase exceeds ~50k LOC or the team has 3+ developers.
306. **Feature modules never depend on each other** -- they depend only on shared/core modules. Communication goes through a coordinator, router, or parent.
317. **Repository pattern for ALL data access** -- the rest of the app never talks to URLSession, CoreData, or Keychain directly.
328. **Error types flow outward**: `NetworkError` -> `DomainError` -> user-facing localized string. Never expose raw HTTP codes to the UI.
339. **One ViewModel per screen** (not per view). Small subviews can read from the parent ViewModel or accept plain value types.
3410. **Prefer value types** (structs, enums) for models and state. Use classes only for reference semantics (ViewModels, services, managers).
35
36---
37
38## Decision Logic
39
40Use this flowchart to decide which reference file to consult:
41
42```
43START
44 |
45 v
46Is the question about project-wide architecture or choosing a pattern?
47 YES -> Read this file (SKILL.md) first, then the relevant reference.
48 NO -> Continue below.
49 |
50 v
51Is the question about MVVM, @Observable, ViewModels, or View-ViewModel binding?
52 YES -> Read references/mvvm.md
53 |
54 v
55Is the question about Clean Architecture, layers, Use Cases, domain entities, or DTOs?
56 YES -> Read references/clean.md
57 |
58 v
59Is the question about TCA, Reducers, Store, @ObservableState, Effects, or ComposableArchitecture?
60 YES -> Read references/tca.md
61 |
62 v
63Is the question about SPM modules, Package.swift, feature modules, or build times?
64 YES -> Read references/modular.md
65 |
66 v
67Is the question about Repository, Coordinator, DI, error handling, POP, or code organization?
68 YES -> Read references/patterns.md
69 |
70 v
71Read the most relevant reference based on context, or consult multiple if the question spans areas.
72```
73
74---
75
76## Folder Structure Templates
77
78### Simple MVVM (Small project)
79
80```
81MyApp/
82├── App/
83│ ├── MyAppApp.swift
84│ └── AppDelegate.swift (if needed)
85├── Features/
86│ ├── Home/
87│ │ ├── HomeView.swift
88│ │ ├── HomeViewModel.swift
89│ │ └── Components/ (small subviews)
90│ ├── Profile/
91│ │ ├── ProfileView.swift
92│ │ └── ProfileViewModel.swift
93│ └── Settings/
94│ ├── SettingsView.swift
95│ └── SettingsViewModel.swift
96├── Models/
97│ ├── User.swift
98│ └── Product.swift
99├── Services/
100│ ├── NetworkService.swift
101│ ├── AuthService.swift
102│ └── Protocols/
103│ ├── NetworkServiceProtocol.swift
104│ └── AuthServiceProtocol.swift
105├── Repositories/
106│ ├── UserRepository.swift
107│ └── ProductRepository.swift
108├── Utilities/
109│ ├── Extensions/
110│ └── Helpers/
111└── Resources/
112 ├── Assets.xcassets
113 └── Localizable.xcstrings
114```
115
116### Clean Architecture (Medium/Large project)
117
118```
119MyApp/
120├── App/
121│ ├── MyAppApp.swift
122│ ├── DIContainer.swift
123│ └── AppCoordinator.swift
124├── Domain/ (NO framework imports)
125│ ├── Entities/
126│ │ ├── User.swift
127│ │ └── Product.swift
128│ ├── UseCases/
129│ │ ├── FetchUserUseCase.swift
130│ │ └── PlaceOrderUseCase.swift
131│ ├── Repositories/ (protocols only)
132│ │ ├── UserRepositoryProtocol.swift
133│ │ └── ProductRepositoryProtocol.swift
134│ └── Errors/
135│ └── DomainError.swift
136├── Data/
137│ ├── Network/
138│ │ ├── APIClient.swift
139│ │ ├── Endpoints/
140│ │ └── DTOs/
141│ ├── Persistence/
142│ │ ├── CoreDataStack.swift
143│ │ └── UserDAO.swift
144│ ├── Repositories/ (implementations)
145│ │ ├── UserRepository.swift
146│ │ └── ProductRepository.swift
147│ └── Mappers/
148│ ├── UserMapper.swift
149│ └── ProductMapper.swift
150├── Presentation/
151│ ├── Features/
152│ │ ├── Home/
153│ │ │ ├── HomeView.swift
154│ │ │ └── HomeViewModel.swift
155│ │ └── Profile/
156│ │ ├── ProfileView.swift
157│ │ └── ProfileViewModel.swift
158│ ├── Navigation/
159│ │ └── Router.swift
160│ └── DesignSystem/
161│ ├── Components/
162│ └── Theme.swift
163└── Resources/
164```
165
166### TCA (Complex state management)
167
168```
169MyApp/
170├── App/
171│ ├── MyAppApp.swift
172│ └── AppFeature.swift (root reducer)
173├── Features/
174│ ├── Home/
175│ │ ├── HomeFeature.swift (State, Action, Reducer)
176│ │ └── HomeView.swift
177│ ├── Profile/
178│ │ ├── ProfileFeature.swift
179│ │ └── ProfileView.swift
180│ └── Auth/
181│ ├── AuthFeature.swift
182│ └── AuthView.swift
183├── Shared/
184│ ├── Models/
185│ ├── Clients/ (Dependencies)
186│ │ ├── APIClient.swift
187│ │ └── UserDefaultsClient.swift
188│ └── Components/
189└── Resources/
190```
191
192---
193
194## Anti-Patterns to Watch For
195
196| Anti-Pattern | Problem | Fix |
197|---|---|---|
198| Massive ViewController/View | Unreadable, untestable | Extract ViewModel + services |
199| ViewModel imports SwiftUI | Couples logic to UI framework | Import Foundation only |
200| Singletons for everything | Hidden dependencies, hard to test | Protocol + DI |
201| Feature A imports Feature B | Tight coupling, circular deps | Shared module or coordinator |
202| Network calls in ViewModel | ViewModel does too much | Repository/Service layer |
203| Force unwrapping optionals | Crashes in production | Guard/if-let + error handling |
204| God model (one huge struct) | Hard to maintain | Split into domain entities |
205| Skipping protocols | Cannot mock, cannot test | Protocol for every external dep |
206
207---
208
209## Testing Strategy per Architecture
210
211| Architecture | Unit Test Target | What to Test |
212|---|---|---|
213| MVVM | ViewModels | State transitions, service calls, error handling |
214| Clean | UseCases + ViewModels | Business logic in isolation, correct layer interaction |
215| TCA | Reducers via TestStore | State changes, effects, action sequences |
216| All | Repositories (with mocks) | Data mapping, caching logic, error propagation |
217
218---
219
220## Quick Decisions
221
222- **@Observable vs ObservableObject?** -> Use @Observable (iOS 17+). Fall back to ObservableObject only for iOS 16 support.
223- **Combine vs async/await?** -> Prefer async/await. Use Combine only for reactive streams (e.g., search debounce, real-time updates).
224- **SwiftData vs CoreData?** -> SwiftData for new projects targeting iOS 17+. CoreData if you need CloudKit advanced features or support iOS 16.
225- **Factory vs Environment for DI?** -> Factory for services/repositories (app-wide). Environment for design-system values (colors, spacing).
226- **Coordinator vs NavigationStack?** -> NavigationStack with a Router @Observable for most SwiftUI apps. UIKit Coordinator only for UIKit-heavy projects.
227- **When to add TCA?** -> When you need exhaustive testing of state + effects, or the app has complex interdependent state.
228- **When to modularize?** -> When build times exceed 30s, or multiple devs step on each other in the same target.
229
230---
231
232## References
233
234Read the appropriate reference file for detailed patterns, code examples, and implementation guidance:
235
236- `references/mvvm.md` -- MVVM with @Observable, ViewModel patterns, testing
237- `references/clean.md` -- Clean Architecture layers, DI, Use Cases
238- `references/tca.md` -- The Composable Architecture patterns
239- `references/modular.md` -- SPM modules, feature modules, build optimization
240- `references/patterns.md` -- Repository, Coordinator, error handling, POP, DI, code organization