Swift Rewind : The Fundamentals of Data Modeling
Season 3 — Episode 3: Enums Unpacked with Associated Values.
Swift Rewind : The Fundamentals of Data Modeling
Season 3 — Episode 3: Enums Unpacked with Associated Values.

Swift enums don’t just represent a fixed set of states — they can also store data along with each case using associated values. This makes them incredibly powerful for modeling real-world scenarios.
What are Associated Values?
Associated values let you attach additional data to enum cases. Each case can have different types and amounts of data.
// Let’s say we want to represent different shapes, each with its own properties:
enum ShapeDimensions {
case square(side: Double)
case rectangle(width: Double, height: Double)
func area() -> Double {
// Using Swift's pattern matching to bind self's associated value with a new variable ( or variables)
switch self {
case let .square(side):
return side * side
case let .rectangle(width, height):
return width * height
}
}
}
let squareShape = ShapeDimensions.square(side: 10.0)
let rectShape = ShapeDimensions.rectangle(width: 5.0, height: 10.0)
//Calling area() on instances created
print("Square area = \(squareShape.area())")
print("Rectangle area = \(rectShape.area())")
🌟 How It Works? — switch is used to pattern match enum cases. let binds associated values to local variables. You can then use those values directly inside the case. Then creating Instances with associated values which requires to specify both case and an appropriate associated value for that case.
Well not All Cases Need Associated Values — Enums can mix cases with and without associated values.
enum ShapeDimensions {
case point // point has no associated value - it is dimensionless
case square(side: Double)
case rectangle(width: Double, height: Double)
func area() -> Double {
switch self {
case .point:
return 0
case let .square(side):
return side * side
case let .rectangle(width, height):
return width * height
}
}
}
let pointShape = ShapeDimensions.point
print("Point area = \(pointShape.area())")
Overall, swift has ability to directly model custom data using your own type in this way is extremely powerful.
Challenge 🚀
Let’s level up our enum skills by adding a perimeter() method & supporting a new shape: right triangle in our ShapeDimensions enum.
enum ShapeDimensions {
case point
case square(side: Double)
case rectangle(width: Double, height: Double)
case rightTriangle(base: Double, height: Double, hypotenuse: Double)
func area() -> Double {
switch self {
case .point:
return 0
case let .square(side):
return side * side
case let .rectangle(width, height):
return width * height
case let .rightTriangle(base, height, _):
return 0.5 * base * height
}
}
func perimeter() -> Double {
switch self {
case .point:
return 0
case let .square(side):
return 4 * side
case let .rectangle(width, height):
return 2 * (width + height)
case let .rightTriangle(base, height, hypotenuse):
return base + height + hypotenuse
}
}
}
// TEST SOLUTION
let square = ShapeDimensions.square(side: 5)
let rectangle = ShapeDimensions.rectangle(width: 4, height: 6)
let triangle = ShapeDimensions.rightTriangle(base: 3, height: 4, hypotenuse: 5)
let point = ShapeDimensions.point
print("Square → Area:", square.area(), "Perimeter:", square.perimeter())
print("Rectangle → Area:", rectangle.area(), "Perimeter:", rectangle.perimeter())
print("Triangle → Area:", triangle.area(), "Perimeter:", triangle.perimeter())
print("Point → Area:", point.area(), "Perimeter:", point.perimeter())
// OUTPUT
Square → Area: 25.0 Perimeter: 20.0
Rectangle → Area: 24.0 Perimeter: 20.0
Triangle → Area: 6.0 Perimeter: 12.0
Point → Area: 0.0 Perimeter: 0.0
🌟 Recursive Enumerations (For the Curious 🧠)
Sometimes, an enum needs to reference itself — this is called a recursive enumeration.
// FamilyTree is recursive because its cases use FamilyTree as an associated value.
indirect enum FamilyTree {
case noKnownParents
case oneKnownParent(name: String, ancestors: FamilyTree)
case twoKnownParents(
fatherName: String,
paternalAncestors: FamilyTree,
motherName: String,
maternalAncestors: FamilyTree
)
}
// Usage
let family = FamilyTree.twoKnownParents(fatherName: "Fred Sr.",
paternalAncestors: .oneKnownParent(
name: "Beth",
ancestors: .noKnownParents
), motherName: "Marsha", maternalAncestors: .noKnownParents
)
But wait why we mentioned “indirect”? — Without indirect, Swift wouldn’t know how much memory to allocate.
Why is that so? — Because each case contains another FamilyTree, which contains another… and so on → infinite size problem. Mentioning indirect solves this by storing the associated value via a reference (pointer). Keeping the enum size fixed and manageable.
// Alternative: Apply indirect to Specific Cases
enum FamilyTree {
case noKnownParents
indirect case oneKnownParent(name: String, ancestors: FamilyTree)
indirect case twoKnownParents(
fatherName: String,
paternalAncestors: FamilyTree,
motherName: String,
maternalAncestors: FamilyTree
)
}
Recursive enums are perfect for modeling nested or tree-like data, such as: Family trees, File systems, UI hierarchies. And indirect makes it all possible by avoiding infinite memory issues.
⭐️ You’ve just completed Episode 3 of Season 3:
In this episode, we explored enums with associated values, learned how to model real-world data more flexibly, and even touched on recursive enums to represent nested structures.
Stay tuned — more calm, clear, and practical Swift concepts are coming next. Let’s keep rewinding Swift, one clean concept at a time.
💬 Found this helpful? Give this post a like and share it with a fellow iOS developer revisiting Swift fundamentals.
메타데이터
- post_id
- 7fbd939ab8cb
- slug
- swift-rewind-the-fundamentals-of-data-modeling-7fbd939ab8cb
- url
- https://medium.com/@krishhna.ios/swift-rewind-the-fundamentals-of-data-modeling-7fbd939ab8cb
- canonical_url
- https://medium.com/@krishhna.ios/swift-rewind-the-fundamentals-of-data-modeling-7fbd939ab8cb
- author_url
- https://medium.com/@krishhna.ios
- status
- ok
- fetched_at
- 2026-06-23 17:05:31