CFA Reference: Glossary and Prohibited Patterns
Formal CFA AI Toolkit for iOS Documentation · Reference 11 of 26
This page collects the CFA vocabulary used across the reference, then lists the prohibited patterns from the CFA specification. A review rejects or refactors any of them.
Definitions
- AppModel: The composition root that creates and exposes the concrete Features.
- Feature: The concrete @MainActor owner of business rules, domain data, operation state and errors.
- Feature API: A Feature's own non-private members; never a separate protocol.
- ViewModel: A dedicated, screen-owned @MainActor @Observable class.
- Repository: A domain-language storage protocol and its actor implementation.
- Change gate: The completion checklist every change must pass.
- Completion record: The evidence recorded at the end of a change: navigator maps, feature and concurrency inventories, build and tests at the final commit, and a human architecture review.
- Shared in-flight task: One retained Task that overlapping callers await, cleared in defer.
- Workflow mode: new-product development or established-product maintenance, recorded once in the project's instructions.
Rules
- Reject business calculations, classifications, validation or filtering in a View.
- Reject reusable business rules in a ViewModel, and one ViewModel shared by multiple unique screens.
- Reject ViewModels stored by AppModel, a root ViewModel or another ViewModel, and Views receiving ViewModels, Features, AppModel or repositories.
- Reject ViewModels depending on the whole AppModel instead of the smallest coherent set of narrow feature APIs.
- Reject Features containing navigation, sheets, dialogs or screen names, and repositories imported directly by Views or ViewModels.
- Reject deep chains through the AppModel object graph, default live dependencies hidden in AppModel.init, and asynchronous side effects inside initialisers.
- Reject Task handles stored by a SwiftUI View, and unstructured Tasks whose lifespan or cancellation affects correctness but is not stored and managed.
- Reject vague lifecycle or action names such as start, begin, process and prepareForUse, and speculative folders, layers or abstractions.
- Reject @unchecked Sendable used as a compiler-warning escape hatch, and serialising independent work when the required behaviour is concurrent.
- Reject Combine observation where Observation is sufficient, competing copies of shared feature state in multiple ViewModels, and Feature APIs exposing directly mutable domain collections.
- Reject domain types placed under storage merely because they are persisted, swallowed business or persistence failures, and expensive or blocking work on the main actor.
Decision rules
- When a review finds a prohibited pattern, refactor it before the change is called complete.
- When a pattern is not listed but adds a second owner of state, treat it as prohibited.
- When a name is vague, rename it for its work: refresh(), save(), retry().
- When a term on this page is unclear in code, look up its reference page in the links below.
Example
The compiled snippet below shows the allowed alternative to two prohibited patterns: the Feature exposes habits as private(set), not a directly mutable collection, and the ViewModel holds a narrow Feature dependency instead of the whole AppModel.
Compiled example
import Observation
struct Habit: Sendable, Equatable {
var name: String
}
@MainActor
@Observable
final class HabitsFeature {
private(set) var habits: [Habit] = []
func addHabit(_ habit: Habit) {
habits.append(habit)
}
}
@MainActor
@Observable
final class HabitsViewModel {
private let habitsFeature: HabitsFeature
init(habitsFeature: HabitsFeature) {
self.habitsFeature = habitsFeature
}
var habitCount: Int { habitsFeature.habits.count }
}Compiled with Swift 6 language mode and warnings treated as errors: type-checked on Swift 6.2 and built on Swift 6.4 (Linux toolchains, Foundation and Observation only).
Common mistakes
- Reading the list as style advice. Each item is a review rejection.
- Fixing a prohibited pattern by renaming it, for example turning a Manager into a Service with the same responsibilities.
- Replacing @unchecked Sendable with nonisolated(unsafe), which is the same escape hatch.
