Swift Enums: Associated Values vs Raw Values Explained
Swift enums can do much more than just list named cases. Two of the most important enum features are raw values and associated values, and understanding the difference helps you model data correctly, write safer code, and avoid common compiler errors. In this article, you will learn what each feature does, how they differ, when to use each one, and how to apply them in realistic Swift 5+ code.
Quick answer: Use raw values when each enum case maps to one fixed underlying value such as a string or integer. Use associated values when each case needs to carry extra data that can vary at runtime, such as an ID, message, or coordinates.
Difficulty: Beginner
Helpful to know first: You will understand this better if you already know basic Swift syntax, how enums work at a simple level, and how switch statements match enum cases.
1. What Are the Options?
Swift enums support two different ways to attach meaning to cases: raw values and associated values. They solve different problems, even though beginners often confuse them.
- Raw values give every case one predefined value of the same type.
- Associated values let a case store extra data that can be different each time you create that case.
- Raw values are fixed as part of the enum definition.
- Associated values are provided when you create an enum value.
- An enum can use associated values or raw values, but a single case does not use both as the same mechanism.
For example, a traffic light enum is a good fit for raw values because each case can map to a stable string like "red" or "green". A server response enum is a good fit for associated values because a success case may need to store a username, token, or other returned data.
2. Side-by-Side Comparison
| Feature | Raw Values | Associated Values |
|---|---|---|
| Purpose | Map each case to one fixed value | Store extra data for a specific enum instance |
| When assigned | In the enum definition | When creating the case value |
| Can vary per instance? | No | Yes |
| Common types | String, Int, Double | Almost any Swift type |
| Access pattern | case.rawValue | Pattern matching with switch or if case |
| Reverse lookup | Often supported with Enum(rawValue:) | Not automatic |
| Typical use | Status codes, API strings, stored identifiers | Responses, state machines, rich domain data |
3. Key Differences Explained
Fixed value vs stored payload
A raw value is part of the enum case definition itself. Every time you use that case, its raw value is the same. An associated value is a payload attached to one конкрет enum instance, so it can change from one use to another.
Here is a raw value enum:
enum Direction: String {
case north = "N"
case south = "S"
case east = "E"
case west = "W"
}
Each direction always has the same string code.
Here is an associated value enum:
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
Each barcode value can carry different data depending on the case and the specific instance.
How you read them
Raw values are easy to read using the rawValue property.
let direction = Direction.north
print(direction.rawValue)
This prints the fixed raw value for the case.
Associated values are extracted with pattern matching.
let code = Barcode.qrCode("SWIFT-123")
switch code {
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check)")
case .qrCode(let value):
print("QR code: \(value)")
}
This works because associated values are not available through rawValue; they must be matched and unpacked.
When to use each
Choose raw values when your enum cases represent stable codes, labels, or identifiers. Choose associated values when cases need to carry meaningful data that belongs to a specific case instance.
- Use raw values for things like file formats, API status names, weekdays, and persisted string codes.
- Use associated values for things like network results, media items, screen state, and parser tokens.
4. When to Use Each
Use raw values when the case has one fixed representation
- Mapping app settings to stored strings
- Converting API text values into Swift types
- Working with numeric codes that should stay consistent
- Displaying a known label or identifier
Example:
enum FileType: String {
case pdf = "application/pdf"
case jpeg = "image/jpeg"
case plainText = "text/plain"
}
Each file type has one exact MIME string, so raw values are a natural fit.
Use associated values when the case needs runtime data
- Returning success with data or failure with an error message
- Representing user actions that carry IDs or text
- Modeling state such as loading, loaded data, or failed request
- Storing different data shapes under one enum type
Example:
enum LoginResult {
case success(String)
case failure(String)
}
Here, the success and failure cases each carry data that changes at runtime.
5. Code Examples
Example 1: Raw values with strings
This example shows an enum whose cases map to fixed strings.
enum Season: String {
case spring = "Spring"
case summer = "Summer"
case autumn = "Autumn"
case winter = "Winter"
}
let currentSeason = Season.summer
print(currentSeason.rawValue)
The output is the raw string Summer.
Example 2: Creating from a raw value
One benefit of raw values is that Swift can often build an enum from the raw value using an initializer.
enum HTTPStatus: Int {
case ok = 200
case notFound = 404
case serverError = 500
}
let status = HTTPStatus(rawValue: 404)
print(status as Any)
This returns an optional because the raw value might not match any case.
Example 3: Associated values for API results
This enum stores different kinds of data depending on the result.
enum APIResponse {
case success(String)
case failure(Int, String)
}
let response = APIResponse.failure(401, "Unauthorized")
switch response {
case .success(let message):
print("Success: \(message)")
case .failure(let code, let reason):
print("Error \(code): \(reason)")
}
The failure case carries both a code and a message, which raw values cannot express.
Example 4: Named associated values
Associated values can be clearer when you name the stored pieces of data.
enum Shape {
case rectangle(width: Double, height: Double)
case circle(radius: Double)
}
let shape = Shape.rectangle(width: 10.0, height: 5.0)
switch shape {
case .rectangle(let width, let height):
print("Rectangle: \(width) x \(height)")
case .circle(let radius):
print("Circle radius: \(radius)")
}
This makes the meaning of each value easier to understand when creating enum instances.
6. Common Mistakes
Mistake 1: Trying to read an associated value with rawValue
Beginners often assume every enum has a rawValue property. That is only true for enums declared with a raw value type.
Problem: This code treats an enum with associated values as if it had fixed raw values, so Swift reports that the member does not exist.
enum ResultState {
case success(String)
case failure(String)
}
let state = ResultState.success("Done")
print(state.rawValue)
Fix: Use pattern matching to extract associated values.
enum ResultState {
case success(String)
case failure(String)
}
let state = ResultState.success("Done")
switch state {
case .success(let message):
print(message)
case .failure(let message):
print(message)
}
The corrected version works because associated values must be unpacked from the enum case.
Mistake 2: Trying to assign different raw values at runtime
Raw values are fixed in the enum definition. You cannot pass a new raw value each time you create a case.
Problem: This code tries to use a raw-value enum case like a function call with changing input, but raw values are not runtime payloads.
enum UserRole: String {
case admin
case guest
}
let role = UserRole.admin("super-admin")
Fix: Use associated values if the case must carry changing data.
enum UserRole {
case admin(String)
case guest(String)
}
let role = UserRole.admin("super-admin")
The corrected version works because associated values allow the case to store data supplied at creation time.
Mistake 3: Forgetting that Enum(rawValue:) returns an optional
When you create a raw-value enum from external input, the input might not match any case.
Problem: This code assumes every integer will map to a valid case, but invalid input produces nil, which can lead to the error Value of optional type must be unwrapped.
enum Priority: Int {
case low = 1
case medium = 2
case high = 3
}
let priority = Priority(rawValue: 5)
print(priority.rawValue)
Fix: Safely unwrap the optional before using it.
enum Priority: Int {
case low = 1
case medium = 2
case high = 3
}
let priority = Priority(rawValue: 2)
if let priority {
print(priority.rawValue)
}
The corrected version works because it handles the possibility that the raw value does not match a case.
7. Tradeoffs and Edge Cases
- Raw values are simpler when you need a stable mapping, but they cannot store changing data.
- Associated values are more expressive, but they require pattern matching to read and compare their contents.
- If you need to create an enum from external text or numbers, raw values are often more convenient because Enum(rawValue:) is built in.
- If cases need different shapes of data, associated values are usually the better design.
- Two enum values with the same case but different associated values are different instances, such as .success("A") and .success("B").
- Raw value enums must use one raw value type for all cases, such as all String or all Int.
- Associated value enums do not automatically support reverse lookup from one value the way raw value enums do.
A common design clue is this: if you are asking, “What fixed code represents this case?” think raw values. If you are asking, “What data should travel with this case?” think associated values.
8. Decision Guide
- If every case has one permanent string, number, or code, use raw values.
- If the case needs to store data provided at runtime, use associated values.
- If you need to build the enum from saved text like "pending" or 200, raw values are usually easier.
- If one case stores one value and another case stores several values, use associated values.
- If you need a compact external representation and rich internal modeling, you may use associated values in one enum and convert to another raw-value enum when needed.
9. Key Points
- Raw values are fixed values defined once for each enum case.
- Associated values are per-instance payloads supplied when creating a case.
- Use rawValue only with raw-value enums.
- Use switch or pattern matching to extract associated values.
- Enum(rawValue:) returns an optional because the lookup can fail.
- Raw values are best for stable identifiers; associated values are best for dynamic data.
10. Final Summary
Swift enum raw values and associated values may look related, but they serve very different purposes. Raw values map each case to one fixed underlying value, which makes them useful for stable identifiers, saved settings, and external codes. Associated values attach runtime data to individual enum instances, which makes them ideal for responses, state, and rich domain models.
When choosing between them, focus on the job your enum needs to do. If the case always means the same fixed string or number, choose raw values. If the case needs to carry data that changes from one instance to another, choose associated values. As a next step, practice writing enums that use switch pattern matching well, since that is the key skill for working confidently with associated values.