Popular Searches
Popular Course Categories
Popular Courses

Using Enumerations, Associated Values, and Raw Values

Using Enumerations, Associated Values, and Raw Values

Object-Oriented Swift and Advanced Language Features

Using Enumerations, Associated Values, and Raw Values in Swift

Course: iOS App Development

Topic: Using Enumerations, Associated Values, and Raw Values

Course Link: iOS App Development Training

Demo Link: Register for a Free Course Demo

Website: JustAcademy


1. Introduction to Enumerations in Swift

Enumerations, commonly called enums, are a Swift data type used to define a group of related values under a single name. They are useful when a variable should represent one value from a predefined set of options.

For example, an iOS application may have different order statuses, payment methods, user roles, network states, or navigation tabs. Instead of representing these values with unrelated strings or numbers, an enumeration provides a structured and type-safe solution.

Swift enumerations are more powerful than simple lists of constants. They can include associated values, raw values, computed properties, methods, initializers, and protocol conformance.

Why use enumerations?

  • Represent a fixed set of related choices.
  • Reduce errors caused by misspelled strings or invalid numbers.
  • Make code easier to read and maintain.
  • Work effectively with Swift's switch statement and pattern matching.
  • Represent complex states and attach relevant data to individual cases.

2. Basic Enumeration Syntax

An enumeration is declared using the enum keyword. Each possible option is defined as a case.

enum Weekday {
    case monday
    case tuesday
    case wednesday
    case thursday
    case friday
    case saturday
    case sunday
}

In this example, Weekday represents the seven days of the week. A variable of this type can hold one of the defined cases.

var today: Weekday = .monday
today = .friday

print(today) // friday

Swift can infer the enumeration type when the context is clear, so the shorthand syntax .friday is valid after the variable's type has been established.

Multiple cases on one line

enum Direction {
    case north, south, east, west
}

Both the multiline and single-line forms define the same four cases. Choose the style that makes the code easiest to understand.

3. Using Enumerations with Switch Statements

The switch statement is commonly used to perform different actions based on an enum case. Swift checks that all possible cases are handled unless a suitable default or other exhaustive pattern is provided.

enum TrafficLight {
    case red
    case yellow
    case green
}

let signal = TrafficLight.green

switch signal {
case .red:
    print("Stop")
case .yellow:
    print("Prepare to stop")
case .green:
    print("Go")
}

Output:

Go

Because the switch handles every case, adding a new case to the enumeration can help the compiler identify switch statements that need updating.

Using an enum in an iOS application

enum AppTab {
    case home
    case search
    case profile
    case settings
}

func openTab(_ tab: AppTab) {
    switch tab {
    case .home:
        print("Opening Home")
    case .search:
        print("Opening Search")
    case .profile:
        print("Opening Profile")
    case .settings:
        print("Opening Settings")
    }
}

openTab(.profile)

This approach is useful for organizing tab navigation, screen selection, and other app behaviors without relying on arbitrary string values.

4. Understanding Associated Values

Associated values allow individual enum cases to carry additional data. Each case can store a different type or combination of types, depending on what the case represents.

For example, a network request can be successful with response data or fail with an error message. These outcomes are related, but the information attached to each outcome is different.

enum NetworkResult {
    case success(String)
    case failure(String)
    case loading
}

let result = NetworkResult.success("Data loaded successfully")

switch result {
case .success(let message):
    print("Success: \(message)")
case .failure(let error):
    print("Error: \(error)")
case .loading:
    print("Loading...")
}

Output:

Success: Data loaded successfully

Here, success and failure each carry a string value, while loading carries no associated data.

4.1 Associated values with different data types

Associated values can contain strings, integers, Boolean values, custom structures, or other types.

enum PaymentMethod {
    case cash
    case creditCard(number: String, expiryYear: Int)
    case online(transactionID: String)
}

let payment = PaymentMethod.creditCard(
    number: "1234-5678",
    expiryYear: 2028
)

switch payment {
case .cash:
    print("Cash payment")
case .creditCard(let number, let year):
    print("Card: \(number), expiry year: \(year)")
case .online(let transactionID):
    print("Transaction: \(transactionID)")
}

Named associated values, such as number and expiryYear, make the meaning of each piece of data clearer.

4.2 Matching only the associated values you need

Use an underscore (_) when a value is not needed in a particular switch case.

enum DownloadStatus {
    case completed(fileName: String, sizeMB: Double)
    case failed(message: String)
}

let status = DownloadStatus.completed(
    fileName: "report.pdf",
    sizeMB: 12.5
)

switch status {
case .completed(let fileName, _):
    print("Downloaded: \(fileName)")
case .failed(let message):
    print("Download failed: \(message)")
}

This avoids introducing unnecessary variables and keeps the code focused on the information being used.

4.3 Matching associated values with conditions

A where clause allows a switch case to include an additional condition.

enum UploadStatus {
    case progress(percent: Int)
    case completed
    case failed
}

let upload = UploadStatus.progress(percent: 85)

switch upload {
case .progress(let percent) where percent >= 80:
    print("Almost complete")
case .progress(let percent):
    print("Uploading: \(percent)%")
case .completed:
    print("Upload completed")
case .failed:
    print("Upload failed")
}

Output:

Almost complete

5. Understanding Raw Values

Raw values are predefined values assigned to enum cases. Unlike associated values, which carry data for a particular enum instance, raw values are fixed values associated with each case of an enumeration.

Raw values are especially useful when an application must map enum cases to stable strings, integers, or other supported literal values.

5.1 String raw values

enum UserRole: String {
    case admin = "Administrator"
    case editor = "Content Editor"
    case viewer = "Read Only"
}

let role = UserRole.admin

print(role.rawValue)

Output:

Administrator

The type declaration : String specifies the raw-value type. Each case can explicitly define its raw value.

5.2 Integer raw values

enum Priority: Int {
    case low = 1
    case medium = 2
    case high = 3
    case critical = 4
}

let priority = Priority.high

print(priority.rawValue)

Output:

3

Integer raw values can be helpful when working with ordered categories or mapping known numeric codes.

5.3 Automatically assigned raw values

Swift can automatically generate raw values for certain enumeration types when you do not explicitly provide them.

Integer raw values: If the first case has no explicit integer raw value, it starts at zero. Subsequent cases increase by one.

enum Level: Int {
    case beginner
    case intermediate
    case advanced
}

print(Level.beginner.rawValue)     // 0
print(Level.intermediate.rawValue) // 1
print(Level.advanced.rawValue)     // 2

String raw values: When the raw-value type is String and no explicit value is assigned, each case's raw value is its case name.

enum Language: String {
    case swift
    case python
    case java
}

print(Language.swift.rawValue) // "swift"

Automatically generated raw values are convenient, but explicit values are often better when values must remain stable for APIs, saved settings, or external data formats.

6. Converting Raw Values into Enum Cases

Swift provides a failable raw-value initializer for raw-value enumerations. It returns an optional because the supplied value may not match any defined case.

enum Environment: String {
    case development = "dev"
    case testing = "test"
    case production = "prod"
}

let selectedEnvironment = Environment(rawValue: "prod")

if let environment = selectedEnvironment {
    print(environment)
} else {
    print("Unknown environment")
}

Output:

production

The raw-value initializer returns an optional Environment?. If the raw value is invalid, the result is nil.

let unknownEnvironment = Environment(rawValue: "staging")

if unknownEnvironment == nil {
    print("Invalid environment value")
}

Output:

Invalid environment value

This technique is particularly useful when reading settings, decoding API responses, or processing values that come from outside the application.

7. Associated Values vs. Raw Values

Although both features attach information to enum cases, they serve different purposes.

Feature Associated Values Raw Values
Purpose Store data for a particular enum value Give each case a predefined value
Data Can vary between instances of the same case Fixed for each case
Declaration Specified in the case's associated-value parameters Specified through the enum raw-value type and optional explicit assignments
Access Usually extracted through pattern matching Accessed through rawValue
Initialization Construct the case with its associated data Use init?(rawValue:) to look up a case
Typical use Network results, errors, payment details Stable identifiers, configuration codes, stored settings

Example comparison

enum Response {
    case success(statusCode: Int, message: String)
    case failure(errorCode: Int)
}

enum HTTPStatus: Int {
    case ok = 200
    case notFound = 404
    case serverError = 500
}

let response = Response.success(
    statusCode: 200,
    message: "Request completed"
)

let status = HTTPStatus(rawValue: 404)

switch response {
case .success(let code, let message):
    print("\(code): \(message)")
case .failure(let errorCode):
    print("Error code: \(errorCode)")
}

print(status?.rawValue as Any)

In this example, the response carries data that may vary for each result, while each HTTP status case has a fixed numeric raw value.

8. Using Enumerations with Computed Properties and Methods

Swift enums can define computed properties and methods. This makes them useful for grouping behavior with a set of related cases.

enum FileType {
    case image
    case video
    case document

    var fileExtension: String {
        switch self {
        case .image:
            return "jpg"
        case .video:
            return "mp4"
        case .document:
            return "pdf"
        }
    }

    func description() -> String {
        return "File type: \(fileExtension)"
    }
}

let type = FileType.image

print(type.fileExtension)
print(type.description())

Output:

jpg
File type: jpg

Computed properties and methods can help centralize logic, reducing duplicated switch statements across an iOS application.

9. Enumerations with CaseIterable

The CaseIterable protocol allows Swift to provide an array containing all cases of a suitable enumeration. It is useful for creating menus, picker options, and filters.

enum AppTheme: CaseIterable {
    case light
    case dark
    case system
}

for theme in AppTheme.allCases {
    print(theme)
}

print(AppTheme.allCases.count)

Output:

light
dark
system
3

CaseIterable can synthesize allCases for simple enums. If an enum has associated values or other characteristics that prevent automatic synthesis, you may need to implement allCases yourself where appropriate.

10. Enumerations in Real-World iOS Development

10.1 Representing API loading states

An iOS application often needs to display a loading indicator, show received data, or present an error. An enum with associated values can represent these states clearly.

enum ViewState {
    case idle
    case loading
    case loaded(items: [String])
    case error(message: String)
}

func display(state: ViewState) {
    switch state {
    case .idle:
        print("Ready")
    case .loading:
        print("Show loading indicator")
    case .loaded(let items):
        print("Display \(items.count) items")
    case .error(let message):
        print("Show error: \(message)")
    }
}

display(state: .loaded(items: ["Apple", "Banana", "Orange"]))

This design keeps the application's possible states explicit and makes it easier to handle each state consistently in a view controller or SwiftUI view model.

10.2 Representing navigation destinations

enum Destination {
    case home
    case product(id: Int)
    case profile(username: String)
}

let destination = Destination.product(id: 101)

switch destination {
case .home:
    print("Navigate to Home")
case .product(let id):
    print("Open product \(id)")
case .profile(let username):
    print("Open profile for \(username)")
}

Associated values allow a navigation destination to carry the information required to open the correct screen.

10.3 Representing application settings with raw values

enum SortOrder: String {
    case ascending = "asc"
    case descending = "desc"
}

func applySortOrder(_ value: String) {
    guard let order = SortOrder(rawValue: value) else {
        print("Unsupported sort order")
        return
    }

    switch order {
    case .ascending:
        print("Sort from low to high")
    case .descending:
        print("Sort from high to low")
    }
}

applySortOrder("desc")

Explicit raw values can help connect Swift code to an API or a stored configuration value. Validate external values rather than assuming they always match the enum.

11. Common Mistakes and Troubleshooting

Mistake Why it happens Solution
Using an invalid case name Enum cases are predefined and case-sensitive. Use the exact case declared in the enum.
Forgetting to extract associated values A case carries additional data. Use a switch pattern or an appropriate conditional pattern to extract the value.
Assuming raw-value initialization always succeeds The input may not match a defined case. Unwrap the optional or use guard let.
Using raw values for changing instance data Raw values are fixed per case. Use associated values when data varies between instances.
Leaving switch cases unhandled A new enum case was added or a case was omitted. Handle all cases or use a suitable default when appropriate.
Changing raw values used by external systems Saved settings or API contracts may depend on them. Use stable explicit raw values and plan migrations if values change.

12. Best Practices for Using Swift Enumerations

  • Choose descriptive enum and case names that communicate meaning.
  • Prefer enums over unrelated strings when the valid options are known.
  • Use associated values when cases need to carry different pieces of data.
  • Use raw values when each case needs a stable predefined identifier.
  • Use explicit raw values when integrating with APIs, databases, or persisted settings.
  • Handle optional results from raw-value initialization safely.
  • Use exhaustive switch statements to benefit from compiler checks.
  • Use CaseIterable when the application needs to iterate through supported cases.
  • Avoid force-unwrapping external values without validation.
  • Keep state-related logic in the enum when it improves clarity and avoids duplicated code.

13. Practical Exercise

Task: Create an enum for an online shopping application's order status. Include associated values for delivery tracking and failure messages, and use a separate raw-value enum for payment status.

Example solution

enum OrderStatus {
    case placed(orderID: Int)
    case shipped(trackingNumber: String)
    case delivered
    case cancelled(reason: String)
}

enum PaymentStatus: String {
    case pending = "PENDING"
    case paid = "PAID"
    case failed = "FAILED"
}

let order = OrderStatus.shipped(trackingNumber: "TRK-1001")
let payment = PaymentStatus(rawValue: "PAID")

switch order {
case .placed(let orderID):
    print("Order placed: \(orderID)")
case .shipped(let trackingNumber):
    print("Tracking number: \(trackingNumber)")
case .delivered:
    print("Order delivered")
case .cancelled(let reason):
    print("Order cancelled: \(reason)")
}

if let paymentStatus = payment {
    print("Payment: \(paymentStatus.rawValue)")
} else {
    print("Unknown payment status")
}

Practice questions

  1. Create a WeatherCondition enum with sunny, cloudy, rainy, and snowy cases.
  2. Create a LoginResult enum with success and failure cases that carry useful associated values.
  3. Create a string raw-value enum for application languages.
  4. Convert a string into an enum using its raw-value initializer and handle invalid input.
  5. Create an enum with a computed property that returns a display label for each case.

14. Interview Questions and Answers

Q1. What is an enumeration in Swift?

An enumeration is a type that groups related cases into a single, type-safe set of possible values.

Q2. What are associated values?

Associated values are additional data attached to a particular enum case. Their values can vary for different instances of that case.

Q3. What are raw values?

Raw values are predefined values associated with enum cases, such as strings or integers. They can be accessed using rawValue.

Q4. What is the difference between associated values and raw values?

Associated values carry instance-specific data, while raw values are fixed for each case. Associated values are commonly extracted through pattern matching; raw values are accessed using rawValue.

Q5. Why does the raw-value initializer return an optional?

Because the provided value might not correspond to any defined case. Returning an optional allows the program to handle invalid values safely.

Q6. Can an enum have methods and computed properties?

Yes. Swift enums can contain methods and computed properties, allowing behavior to be grouped with the related cases.

Q7. What is the purpose of CaseIterable?

CaseIterable allows code to access all cases of an eligible enum through allCases, which is useful for menus, pickers, and filters.

Q8. Why are enums useful in iOS development?

They help model application states, navigation destinations, user selections, API outcomes, errors, and configuration options in a clear and type-safe way.

Q9. Can different enum cases have different associated-value types?

Yes. For example, one case can carry an integer, another can carry a string, and another can carry no associated data.

Q10. How can you handle an invalid raw value?

Use optional binding with if let or guard let to check whether the initializer returned a valid case.

15. Quick Revision Checklist

  • enum declares an enumeration.
  • case declares a possible enum value.
  • Associated values attach variable data to individual cases.
  • Raw values attach predefined values to cases.
  • rawValue reads a case's raw value.
  • EnumType(rawValue: value) attempts to find a matching case and returns an optional.
  • switch helps handle enum cases and extract associated values.
  • where adds conditions to pattern matching.
  • CaseIterable provides access to all cases for eligible enums.
  • Enums make application states and choices easier to maintain.

16. Conclusion

Swift enumerations are an important part of writing reliable iOS applications. Basic cases represent a defined set of choices, associated values carry information specific to a case, and raw values provide stable identifiers for cases. By combining these features with switch statements, computed properties, and protocols such as CaseIterable, developers can build clearer models for navigation, API responses, payment flows, application settings, and other real-world requirements.

17. Course Resources

Explore more about iOS development and Swift through these JustAcademy resources:

Previous Next
whatsapp