| name | swift-cognitive-complexity |
| description | Measure and reduce Cognitive Complexity in Swift code based on "Cognitive Complexity — A new way of measuring understandability" by G. Ann Campbell (SonarSource). Use when the user asks to check cognitive complexity, reduce nesting, simplify control flow, untangle complex conditions, or make Swift functions easier to understand. |
| argument-hint | ["file or code snippet"] |
| allowed-tools | ["Read","Bash","Edit","Glob","Grep"] |
Swift Cognitive Complexity
Measure and reduce Cognitive Complexity in Swift code per G. Ann Campbell's paper (SonarSource, 2023).
Cognitive Complexity measures mental effort to understand code — not just path count (Cyclomatic Complexity). Deeply nested code costs more than flat code with the same branch count.
Arguments
The user provided: $ARGUMENTS
If a file path is given — read it. If no arguments — ask the user to paste the code or specify a file.
Scoring Rules
A — Structural increments (+1 each, plus nesting penalty)
These structures break linear flow and add nesting:
| Swift construct | Increment |
|---|
if, else if, else | +1 (+ nesting level) |
guard … else | +1 (+ nesting level) |
switch | +1 (+ nesting level) |
for, while, repeat…while | +1 (+ nesting level) |
catch | +1 (+ nesting level) |
Ternary ? : | +1 (+ nesting level) |
| Nested closure / trailing closure | +1 (+ nesting level) |
| Recursive call | +1 |
B — Nesting penalty
Every structural increment inside another structural scope adds +1 per nesting level:
func f() {
if a { // +1 (level 0)
for b in c { // +2 (level 1 → +1 base + 1 nesting)
if d { // +3 (level 2 → +1 base + 2 nesting)
}
}
}
}
// Total: 6
Closures count as an extra nesting level:
items.forEach { item in
if item.isValid {
item.process()
}
}
C — Flat increments (+1, no nesting penalty)
These add complexity without adding nesting depth:
| Swift construct | Increment |
|---|
break / continue to label | +1 |
| Sequence of mixed logical operators | +1 per mixed sequence |
Mixed logical operators: each contiguous sequence of the same operator is one increment; switching operator type starts a new increment.
if a && b && c { }
if a && b || c { }
if a && b || c && d || e { }
Thresholds (SonarQube defaults)
| Score | Signal |
|---|
| 0–15 | Good |
| 16–25 | Review candidate |
| 25+ | Refactor |
Key Differences from Cyclomatic Complexity
| Cyclomatic | Cognitive |
|---|
| Switch with 5 cases | +5 | +1 |
&& and || per operator | +1 each | +1 per mixed sequence |
| Nesting depth | ignored | multiplier |
| Recursion | ignored | +1 |
| Nested closures | ignored | +1 per level |
Swift-Specific High-Complexity Patterns
1. Nested closures
fetchUser { user in
fetchOrders(for: user) { orders in
orders.forEach { order in
if order.isPending {
}
}
}
}
let user = try await fetchUser()
let orders = try await fetchOrders(for: user)
for order in orders where order.isPending { }
2. Guard stacking vs nested ifs
func process(_ input: String?) {
if let input = input {
if !input.isEmpty {
if input.count > 3 {
handle(input)
}
}
}
}
func process(_ input: String?) {
guard let input = input else { return }
guard !input.isEmpty else { return }
guard input.count > 3 else { return }
handle(input)
}
3. Complex boolean conditions
if user.isActive && !user.isBanned || user.isAdmin && config.allowAdminOverride { }
if user.canAccess(with: config) { }
4. Switch with side effects
switch state {
case .loading:
if retryCount > 3 {
showError()
} else {
retry()
}
case .ready:
if data.isEmpty {
showEmpty()
}
default: break
}
switch state {
case .loading: handleLoading()
case .ready: handleReady()
default: break
}
Instructions
- Read the provided Swift file(s)
- For each function/method/closure, calculate the Cognitive Complexity score:
- Walk the body, tracking current nesting depth
- Apply rules A, B, C above
- Sum all increments
- Report each function with score ≥ 5:
## [FileName.swift]
| Function | Score | Threshold |
|---|---|---|
| processPayment() | 28 | ⛔ Refactor |
| validateInput() | 18 | ⚠️ Review |
| formatDate() | 4 | ✅ Good |
### processPayment() — Score: 28
Breakdown:
- Line 12: `if status == .pending` → +1 (level 0)
- Line 14: `for item in cart` → +2 (level 1)
- Line 16: `if item.isValid` → +3 (level 2)
- Line 19: `a && b || c` → +2 (mixed operators)
...
Suggested refactoring:
[specific extract-function suggestions]
-
Apply fixes using Edit tool:
- Extract deeply nested closure bodies into named functions
- Replace nested
if let chains with guard statements
- Extract complex boolean conditions into named computed properties or functions
- Extract switch case bodies into dedicated functions
-
Recalculate scores after refactoring and show before/after comparison