top of page

CFA Reference: Review Checklist

Formal CFA AI Toolkit for iOS Documentation · Reference 20 of 26

The CFA specification ends with a review checklist. Every applicable answer must be yes. Marking a question not applicable needs a short explanation; otherwise the change is not architecturally complete.

Definitions

  • Applicable: A question that concerns something the change touches.
  • Architecturally complete: Every applicable checklist answer is yes.

Rules

  1. Can the folder location alone explain which layer owns every new type?
  2. Does every unique screen have its own adjacent ViewModel?
  3. Is each ViewModel named viewModel, owned with @State, and released with its View?
  4. Can every View be created without receiving a ViewModel, AppModel, Feature or repository, and do screen Views avoid receiving another screen's actions?
  5. Does every business decision live in a Feature or lower Model layer?
  6. Would an Apple Watch app be able to reuse the decision without copying UI code?
  7. Does each ViewModel depend on the smallest coherent set of narrow, replaceable feature APIs?
  8. Does AppModel only assemble and expose Features or coordinate a genuinely application-wide workflow?
  9. Are persistence and external-system details behind repository contracts?
  10. Are async operations directly testable, with no Task handle stored by a View and every meaningful Task stored by its correct owner?
  11. Has required concurrency behaviour been preserved rather than simplified away?
  12. Are time-dependent business rules deterministic in tests?
  13. Does every ViewModel have its own focused test suite?
  14. Can a junior developer read the names as plain English without learning the entire object graph first?
  15. Was every new abstraction earned by a concrete responsibility?
  16. Does each piece of shared state have one authoritative owner and mutate only through named feature commands?
  17. Can every meaningful Task lifespan (durable, replaceable, presentational or long-lived) be explained and managed appropriately?
  18. Is expensive work kept away from the main actor while observable results return to it safely?
  19. Are failures, partial loading, offline persistence and retry behaviour represented honestly?
  20. Are domain types stored with the feature that owns their meaning?

Decision rules

  • When an answer is no, fix the change before accepting it.
  • When a question does not apply, write why.
  • When you want this checked without changes, ask CFA Architecture Review.

Example

Applied to the compiled example in References 4–6: HabitsViewModel is screen-owned (2), it depends only on habitsFeature (7), AppModel only assembles Features (8), storage sits behind HabitsRepository (9), the refresh task is stored by the Feature and the ViewModel (10, 17), and errors are retained separately (19).

Compiled example (excerpt)

import Foundation
import Observation

// MARK: Screen ViewModel
@MainActor
@Observable
final class HabitsViewModel {
    private let habitsFeature: HabitsFeature
    @ObservationIgnored private var refreshTask: Task<Void, Never>?
    var draftName = ""
    private(set) var saveError: String?

    init(habitsFeature: HabitsFeature = AppModel.shared.habitsFeature) {
        self.habitsFeature = habitsFeature
    }

    var habitNames: [String] { habitsFeature.habits.map(\.name) }
    var isLoading: Bool { habitsFeature.isLoading }
    var loadError: String? { habitsFeature.loadError }

    /// Repeated presses reuse the one retained task.
    @discardableResult
    func refresh() -> Task<Void, Never> {
        if let refreshTask { return refreshTask }
        let habitsFeature = habitsFeature
        let newRefreshTask = Task {
            await habitsFeature.refresh()
            self.refreshTask = nil
        }
        refreshTask = newRefreshTask
        return newRefreshTask
    }

    func save() async {
        do {
            try await habitsFeature.addHabit(named: draftName)
            draftName = ""
            saveError = nil
        } catch {
            saveError = "The habit could not be saved."
        }
    }
}

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

  • Answering yes from memory instead of reading the code.
  • Marking questions not applicable without a reason.
  • Treating the checklist as optional for small changes.

Links

bottom of page