DEV Community

Cover image for Swift Enums Explained: A Detailed Overview
Matthew Ogtong
Matthew Ogtong

Posted on • Originally published at ogtongm.Medium

Swift Enums Explained: A Detailed Overview

Swift enums offer a robust and adaptable feature in iOS development. They let developers establish a type with a limited set of cases, enhancing code's expressiveness, readability, and maintainability. In this article, we'll explore Swift enums in depth, demonstrating their potential and how they can be employed to address challenges in your work.From handling app states to harnessing associated values, you'll discover how to optimize Swift enums and apply them efficiently in your daily development endeavors.

Understanding Enums: The Basics

Enumerations, commonly referred to as enums, are an essential component of Swift programming. Enums enable you to define a type that has a restricted set of potential cases, with each case signifying a unique value or state within the enumeration. Enums come in handy when representing related values, like days of the week, months, or even various app states.

Check out this straightforward example of an enum representing the days of the week:

To utilize enums, you can allocate a case to a variable and then incorporate it into your code:

Now that we've covered the basics, let's take a look at associated values and learn how they can add more functionality to your enums.

Raw Values and Initializing Enums

Enums in Swift offer different ways to work with values, including raw values and associated values. In this section, we'll discuss raw values and how to initialize enums using them. Raw values are default values that can be assigned to each case in an enum. Enums with raw values can automatically receive initializers that take a raw value as an argument.

Typically, raw values are used when there's a need to represent each case with a unique, pre-defined value. Let's explore this concept with an example representing HTTP response status codes:

In this example, the HttpStatusCode enum has an Int raw value type. Each case is assigned a unique integer value, representing the specific HTTP response status code.
Using raw values, it's simple to create an instance of the enum by supplying a raw value during initialization:

Keep in mind that initializing an enum with a raw value results in an optional, as the raw value given might not match any of the enum's cases. This is beneficial when addressing situations where the raw value could be invalid or unknown:

Utilizing raw values in enums offers a direct method to represent and work with unique, pre-determined values, which simplifies managing enums in real-life situations, such as processing HTTP response codes within a networking app.

Enums with Associated Values

Swift enums have the capability to store associated values, which enables you to connect extra data to each case. This functionality further enhances the versatility and potency of enums, as you can store various data types depending on the specific case.

Take a look at the following example which utilizes enums that sends requests to an API:

In this example, we create an APIRequest enum with three cases: get, post, and delete. Each case carries an associated value containing a URL string, while the post case has an extra associated value holding a dictionary of parameters.

When working with associated values, you can employ a switch statement to extract the values and manage them as needed:

In this example, we create an instance of the APIRequest enum with a post case and pass the URL and parameters as associated values. Then, we use a switch statement to match the case and extract the associated values, printing the request details accordingly.

Enums with associated values can help organize and manage app data in a more structured and expressive way, making your code easier to understand and maintain.

Enums and Methods

In Swift, enums can also have methods associated with them. This allows you to bundle functionality within your enums, enhancing their capabilities and flexibility. Incorporating methods into enums contributes to the creation of neat and orderly code, which is particularly advantageous when dealing with extensive projects. This approach helps developers create easy-to-understand and maintainable code, keeping the complexity at bay while working on real-world projects.

Consider an enum that represents different types of transportation:

In this example, we have a Transportation enum that encompasses three options: car, train, and bicycle. Each of these cases comes with a respective speed value. Moreover, we've integrated a travelTime method that calculates the duration needed to traverse a specific distance, factoring in the speed of the selected transportation type.

To utilize the travelTime function, you can establish an instance of the Transportation enum and invoke the method like so:

By integrating methods within enums, you can produce more streamlined and structured code. This approach simplifies the comprehension and upkeep of your code, offering significant advantages when tackling intricate projects or working alongside a team.

Enums and Extensions

Extensions in Swift let you introduce new capabilities to existing types, including enums. By applying extensions to enums, you can distinguish your enum declaration from extra methods or computed properties, resulting in more modular and manageable code.

Take a look at this example of a Theme enum, which signifies various color themes for an app:

In this case, we have defined a Theme enum with two cases: light and dark. We then employed an extension to include a computed property named backgroundColor within the enum. This property delivers a UIColor according to the active theme.

To utilize the backgroundColor computed property, create an instance of the Theme enum and access it as follows:

Merging enums with extensions allows you to produce more structured and adaptable code, streamlining the process of updating and maintaining your projects.

Making Enums Iterable with CaseIterable

Sometimes, you may want to iterate through all the cases of an enum. Swift provides a simple way to achieve this by conforming your enum to the CaseIterable protocol. This protocol automatically generates an array of all the cases in your enum, making it easy to loop through them.

Let's take a look at an example using a Season enum:

By conforming to the CaseIterable protocol, we gain access to the allCases property, which is an array of all the cases in the enum:

This code snippet will print each season in the order they are defined in the enum. Utilizing CaseIterable with enums can be particularly useful when you need to perform an action for each case or dynamically populate UI elements based on the enum cases.

With CaseIterable, you can easily work with all the cases of an enum, simplifying tasks such as creating dropdown menus or performing actions for each case in a more structured and maintainable manner.

Managing App State with Enums

Enums are a fantastic resource for handling your app's state, as they clearly illustrate various states and make your code more readable and straightforward. Utilizing enums to depict app states ensures your app operates correctly based on its current state and streamlines state transitions.

Suppose you're developing an iOS app that retrieves data from a remote API. You might encounter different states like loading, loaded, or error. You can employ an enum to portray these states and oversee them throughout your app:

By employing an enum with associated values, you can preserve pertinent data for each state. For instance, you can keep the retrieved data in the loaded case and an Error object in the error case.

To modify the app state, you can use a function that accepts the new state as a parameter and executes the required actions:

Using enums enables a more organized and methodical method for handling app state, simplifying the comprehension of your app's flow and guaranteeing its correct behavior in various situations.


Conclusion

Throughout this article, we've explored various aspects of enums in Swift, showcasing their power and versatility when it comes to organizing and structuring your code. We delved into the basics of creating and using enums, raw values and initializing from raw values, associated values, enums with methods, enums and extensions, CaseIterable protocol, and managing app state using enums.

By understanding and applying these concepts in your projects, you can create cleaner, more modular code that's easier to maintain and comprehend. Enums are an invaluable tool in your iOS development toolkit, and their proper utilization can significantly improve your overall coding experience. So, make sure to take advantage of Swift enums and let them work their magic in your future projects!


Resources

Top comments (2)

Collapse
 
jimmymcbride profile image
Jimmy McBride

One little trick I've learned in Kotlin with making States easier to deal with is to create an extension function to help clean up state management in code. In swift it would look something like this:

extension AppState {
    func duringAppState(loading: (() -> Void)? = nil, success: (([String]) -> Void)? = nil, error: ((Error) -> Void)? = nil) {
        switch self {
        case .loading:
            loading?()
        case .success(let data):
            success?(data)
        case .error(let error):
            error?(error)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Then we can use it in our code to handle what happens in each state. An example of using this extension function in our app would looks something like this:

let state = AppState.success(data: ["one", "two", "three"])

state.duringAppState(
    loading: {
        print("loading...")
    },
    success: { data in
        print("success: \(data)")
    },
    error: { error in
        print("error: \(error.localizedDescription)")
    }
)
Enter fullscreen mode Exit fullscreen mode

So instead of having to set up a switch statement each time, we can directly call our extension function and handle the code inside each block with having access to the specific data to each state as a lambda in the block.

Collapse
 
matthewogtong profile image
Matthew Ogtong

Thanks for sharing this Jimmy!
Your extension function makes the implementation much cleaner and more concise. I'm actually planning to write a blog post on swift extensions and protocols next week, and I'll be sure to incorporate your valuable insight into it. As always I appreciate the tips 🙏