Swift is a language that rewards clarity and safety. Most of the time, its features feel obvious once you see them in action.
The indirect keyword is not one of those features.
At first glance, it feels obscure. You rarely see it in day-to-day app development. But when you do need it, nothing else quite replaces it.
This article breaks down:
indirectindirect?The indirect keyword in Swift is used with enumerations to enable recursive data structures.
It tells the compiler:
“Store this case indirectly (via a reference), not inline.”
This matters when an enum case refers to itself, either directly or indirectly.
It’s not just a keyword.
It’s a design escape hatch for a very real limitation of value types.
indirect SolvesLet’s start with a simple attempt:
enum Node {
case value(Int)
case branch(Node, Node)
}This will not compile.
Because Swift enums are value types, and value types require a known, finite size at compile time.
But here:
case branch(Node, Node)Node contains Node, which contains Node, which contains Node…
This creates an infinite size problem.
The compiler cannot determine how much memory to allocate.
indirectWe fix this by marking the recursive case as indirect:
enum Node {
case value(Int)
indirect case branch(Node, Node)
}Now it compiles.
The branch case is no longer stored inline. Instead, Swift stores it indirectly, typically using a heap allocation and reference.
This breaks the infinite recursion problem.
In other words:
indirect → everything is stack-like, inline, predictableindirect → you introduce heap allocation and indirectionYou are trading:
✅ Infinite structure support
❌ For absolute memory predictability
That trade is intentional.
indirectYou have two options:
enum Node {
case value(Int)
indirect case branch(Node, Node)
}This is precise. You only pay the cost where needed.
indirect enum Node {
case value(Int)
case branch(Node, Node)
}This works but it’s often overkill.
If you mark the entire enum as indirect, you’re probably not thinking hard enough about your data model.
This is where indirect becomes genuinely useful.
Let’s model mathematical expressions:
indirect enum Expression {
case number(Int)
case addition(Expression, Expression)
case multiplication(Expression, Expression)
}This is not just an enum.
This is a tree.
let expr = Expression.multiplication(
.addition(.number(2), .number(3)),
.number(4)
)This represents:
(2 + 3) * 4Now let’s evaluate it:
func evaluate(_ expression: Expression) -> Int {
switch expression {
case .number(let value):
return value
case .addition(let left, let right):
return evaluate(left) + evaluate(right)
case .multiplication(let left, let right):
return evaluate(left) * evaluate(right)
}
}This is a recursive function operating on a recursive data structure:
.number → base case.addition → evaluate both sides, then add.multiplication → evaluate both sides, then multiplylet result = evaluate(expr)
print(result) // 20This is clean, expressive, and type-safe.
No string parsing. No runtime ambiguity.
indirect MattersWithout indirect, you cannot model:
Even though indirect uses references internally, the enum still behaves like a value type externally.
This means:
Without indirect, you'd need something like:
final class Box<T> {
let value: T
init(_ value: T) {
self.value = value
}
}And then:
enum Node {
case value(Int)
case branch(Box<Node>, Box<Node>)
}This is:
indirect removes this boilerplate entirely.
When you mark a case as indirect, Swift:
So instead of:
Enum contains full nested structure inlineYou get:
Enum → pointer → actual dataThis is what breaks the infinite size recursion.
You lose stack-only guarantees.
Extra pointer lookups. Usually negligible but real.
Memory graphs become less obvious.
If you’re building:
You don’t have a better alternative.
indirect in recursive enumsenum Tree {
case leaf(Int)
case node(Tree, Tree) // ❌ compiler error
}Fix:
enum Tree {
case leaf(Int)
indirect case node(Tree, Tree)
}indirectNot every enum needs it.
enum Result {
case success(Int)
case failure(Error)
}Adding indirect here is unnecessary and harmful.
Even with indirect, this is still a value type:
var a = expr
var b = aMutating b does not affect a.
indirect enum FileSystemItem {
case file(name: String)
case folder(name: String, contents: [FileSystemItem])
}let system = FileSystemItem.folder(
name: "root",
contents: [
.file(name: "README.md"),
.folder(
name: "src",
contents: [
.file(name: "main.swift")
]
)
]
)This is exactly how you should model hierarchical data.
Not with dictionaries.
Not with loosely typed arrays.
Not with classes by default.
indirect?Use it when:
Avoid it when:
indirect is not a beginner feature.
It’s a modeling tool.
Most developers don’t need it because most developers don’t model complex data structures.
But if you’re building:
Then indirect stops being optional.
The key is simple:
Reach for
indirectonly when your data structure demands recursion.
If you have suggestions or opinion, feel free to connect with me on X and send me a DM. If this article helped you, Buy me a coffee.