Swift enumerations (enums) are a powerful and expressive feature that allows you to represent a group of related values in a type-safe manner. Enums play an essential role in organizing your code and making it more readable and maintainable. They are used heavily for SwiftUI pickers. In this blog post, we will dive deep into understanding Swift enums by exploring their raw values, associated values, and various use cases. We will also discuss best practices and techniques to make enums more convenient and powerful, including functions, computed properties, and conforming to protocols like CaseIterable and Identifiable. So, buckle up and get ready to enhance your Swift programming skills with the power of enumerations!
Enumeration in Swift
Enumeration is a powerful feature in Swift that allows developers to define a group of related values in a single type. It provides a way to organize code and make it more readable and maintainable. In Swift, enumeration is defined using the “enum” keyword, followed by a set of cases that represent the possible values for the enumeration.
What is an enum type in Swift?
An enum type is a data type that represents a finite set of predefined values, often referred to as cases. Enums can be used to make your code more readable, maintainable, and less error-prone.
What is the syntax of enumeration?
To define an enumeration in Swift, use the enum keyword followed by the name of the enumeration and a set of cases enclosed in curly braces:
enum CompassPoint {
case north
case south
case east
case west
}
What are the advantages of enums in Swift?
Enums offer several advantages in Swift, such as:
- Improved code readability and clarity
- Type safety, reducing the likelihood of errors
- Easily adaptable to changes in requirements
Using Enums with Switch Statement
Enums can be used in switch case statement to simplify complex conditional logic. Here is an example in SwiftUI, where a different text is shown for the enum cases:
import SwiftUI
enum CompassPoint {
case north
case south
case east
case west
}
struct ContentView: View {
let direction = CompassPoint.north
var body: some View {
VStack(spacing: 40) {
Text("You will reach your destination in 15 min")
.bold()
switch direction {
case .north:
Text("Heading North")
case .south:
Text("Heading South")
case .east:
Text("Heading East")
case .west:
Text("Heading West")
}
}
.font(.largeTitle)
}
}
5 Examples Use Cases of Enum in iOS Apps
Days of the week for selecting a regular meeting time in a reminders app:
enum Weekday: Int {
case sunday = 1, monday, tuesday, wednesday, thursday, friday, saturday
}
Setting the temperature unit in a weather app:
enum TemperatureUnit: String, CaseIterable {
case celsius, fahrenheit, kelvin
}
Select a gender for setting up an user profile:
enum Gender: String {
case male, female, other, preferNotToSay
}
Selecting a currency for a showing prices in a shopping app:
enum Currency {
case usd, eur, gbp, jpy, aud, cad, chf, cny, sek, nzd
}
Selecting a food categories for a food delivery app:
enum FoodCategory: String, CaseIterable {
case italian
case chinese
case indian
case american
}
Using Enum Values in Your SwiftUI Together with Picker View
To use this enum in a picker, we can create a ForEach view that iterates over all the cases of the enum and creates a Text view for each case. When the user selects a category from the picker, the selectedCategory variable is updated with the new value, and the picker displays the selected category as its current value.
import SwiftUI
enum FoodCategory: String, CaseIterable, Identifiable {
case italian = "Italian"
case chinese = "Chinese"
case indian = "Indian"
case american = "American"
var id: Self { self }
}
struct ContentView: View {
@State private var selectedCategory: FoodCategory = .italian
var body: some View {
VStack(spacing: 50) {
Text("Selected Category: \(selectedCategory.rawValue)")
Picker("Food Category", selection: $selectedCategory) {
ForEach(FoodCategory.allCases) { category in
Text(category.rawValue)
}
}
}
}
}
If you want to see how to use picker views for your SwiftUI projects, you can check out this blog post.
What are enum associated values in Swift?
Associated values in Swift enums allow you to store extra information for each case of an enumeration. This additional data is provided when you create an instance of the enumeration and can differ for each case, making associated values a powerful tool for representing complex data structures.
Associated values enable you to attach custom information to each case of an enum. These values are specified when you create an instance of the enum and can vary for each case, allowing you to store more context-specific data.
To illustrate the use of associated values in Swift enums, let’s expand the FoodCategory enum:
enum FoodCategory {
case italian(pastaDish: String)
case chinese(dish: String, levelOfSpiciness: Int)
case indian(dish: String, isVegetarian: Bool)
case american(fastFood: String, hasCheese: Bool)
}
Here, we’ve added associated values to each case of the FoodCategory enum. For example, the italian case now has an associated pastaDishvalue of type String. The chinese case has a dish value and a levelOfSpiciness value, and so on.
Using associated values in Swift enums has several advantages:
- Flexibility: Enums with associated values can represent complex data structures and store additional context-specific information.
- Type safety: Since associated values have specific types, they help maintain type safety and reduce the likelihood of errors.
- Readability: Enums with associated values make code more readable by providing a clear representation of different cases and their associated data.
In summary, associated values in Swift enums allow you to create more expressive and powerful data structures, making your code more flexible, type-safe, and readable.
Raw Values in Swift Enums
Raw values in Swift enums are predefined values of the same type that can be associated with each case of the enumeration. These values are unique within the enum and provide a simple way to represent a set of related values.
Raw values are default values that you can associate with each case in an enum. They must be unique and of the same type. Raw values are useful when you want to represent a set of related values with a simple data type, like an integer or a string.
Here’s an example of a Swift enum with integer value:
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
In this example, the Planet enum has integer raw values starting with mercury = 1, and the subsequent cases are assigned consecutive integer values by default.
You can also use string type for a Swift enum:
enum ProgrammingLanguage: String {
case swift = "Swift"
case python = "Python"
case java = "Java"
}
In this example, the ProgrammingLanguage enum has string value for each case. In your app, you could use the raw value to show in a Text view what option was selected. To access the raw value of an enum case, use the rawValue property:
let selectedLanguage = ProgrammingLanguage.swift
Text(selectedLanguage.rawValue)
Benefits to using raw values in Swift enums
- Simplicity: Raw values provide a straightforward way to represent a set of related values with a simple data type.
- Type safety: Enums with raw values offer type safety, reducing the likelihood of errors in your code.
- Readability: Using raw values can make your code more readable by providing a clear representation of each case and its associated value.
Best practices for working with raw values in Swift enums
When working with raw values in Swift enums, consider the following best practices:
- Ensure that raw values are unique within the enum to avoid confusion.
- Use descriptive names for your enum cases to improve code readability.
- Choose an appropriate raw value type (e.g., Int, String) that best represents your data.
Raw values in Swift enums provide a simple and type-safe way to represent a set of related values. By following best practices, you can create clear and expressive enums that make your code more readable and maintainable.
Making Enums More Convenient
Enums in Swift can be enhanced with functions, computed properties, and conforming to protocols like CaseIterable and Identifiable. These additions make enums more flexible, powerful, and convenient to use in your code.
Functions and Computed Properties
You can add functions and computed properties to an enum to provide additional functionality. For example, you can add a computed property to the FoodCategory enum to return a description of each case:
enum FoodCategory {
case italian, chinese, indian, american
var description: String {
switch self {
case .italian:
return "Italian cuisine"
case .chinese:
return "Chinese cuisine"
case .indian:
return "Indian cuisine"
case .american:
return "American cuisine"
}
}
Now, you can access the description using the description property:
let category = FoodCategory.italian
Text(category.description) // shows: "Italian cuisine"
CaseIterable: Easily Accessing all Cases
The CaseIterable protocol allows you to easily iterate through all cases of an enum. To make the FoodCategory enum conform to the CaseIterable protocol, add CaseIterable to the enum declaration:
enum FoodCategory: CaseIterable {
case italian, chinese, indian, american
}
Now, you can access all cases of the FoodCategory
enum using the allCases
property:
for category in FoodCategory.allCases {
print(category.description)
}
How do you make an enum Identifiable?
To make an enum identifiable, conform to the Identifiable
protocol by providing a unique identifier for each case. You can do this by adding an id
computed property to the enum:
import Foundation
enum FoodCategory: CaseIterable, Identifiable {
case italian, chinese, indian, american
var id: Self { self }
var description: String {
switch self {
case .italian:
return "Italian cuisine"
case .chinese:
return "Chinese cuisine"
case .indian:
return "Indian cuisine"
case .american:
return "American cuisine"
}
}
}
Real-World Use Cases for Enums
Enums are a versatile and powerful feature in Swift that can be employed in various scenarios. In this section, we will discuss three real-world use cases for enums: error handling, data modeling, and state management.
Error Handling
Using enums for error handling allows you to represent various error cases in a type-safe manner, making debugging easier. For example:
enum NetworkError: Error {
case requestFailed
case invalidURL
case serverError(code: Int)
}
In this example, NetworkError
represents different error cases that can occur during network communication. Using enums for error handling provides a clear and structured representation of possible errors.
Data Modeling
Enums are useful for modeling complex data structures with different variations. For instance, consider the following example of a PaymentMethod
enum:
enum PaymentMethod {
case creditCard(number: String, expirationDate: String, cvv: String)
case paypal(email: String)
case applePay
}
Here, the PaymentMethod
enum represents different types of payment methods that can be used in an e-commerce app. Using enums for data modeling allows you to create clear and expressive representations of various data structures in your application.
State Management
Enums can be employed to manage the state of an application, providing a clear representation of the current state. For example, consider a simple LoadingState
enum for an app that fetches data:
enum LoadingState {
case idle
case loading
case success
case failure
}
In this example, the LoadingState
enum represents different states of the data-fetching process. Using enums for state management can make your code more readable and maintainable, as the current state of the application can be easily understood.
Conclusion
In summary, Swift enumerations are a versatile and powerful feature that provide a type-safe way to represent a group of related values. Throughout this blog post, we explored the key concepts of Swift enums, such as raw values, associated values, and their respective benefits. We also discussed how to make enums more convenient and expressive by adding functions, computed properties, and conforming to protocols like CaseIterable and Identifiable. By understanding and implementing these concepts, you can enhance your Swift programming skills and create more readable, maintainable, and expressive code. So, go ahead and experiment with Swift enums to harness their full potential in your programming projects!
Further Reading
- Learn more about SwiftUI picker in this blog post SwiftUI Picker Made Easy: Tutorial With Example
- How to create a drop-down picker with Menu
- Format date types in this post Swift Date Formatting: 10 Steps Guide.
FAQ
What is the difference between enum and enumeration in Swift?
There is no difference between enum and enumeration in Swift. They are two different ways of referring to the same thing.
What is raw value and associated value enum in Swift?
A raw value enum in Swift is an enum where each case has a pre-defined raw value. An associated value enum is an enum where each case has a value associated with it.
Can associated values and raw values coexist in Swift enumeration?
No, a Swift enumeration can either have associated values or raw values, but not both.
What are enum associated values?
Enum associated values are values that are associated with each case of an enum. They can be of any type and are used to provide additional information about the case.
What is the function of enum in Swift?
The function of enum in Swift is to define a set of related values that can be used throughout the code.
Can enums have methods in Swift?
Yes, enums can have methods in Swift. These methods can be used to perform operations on the associated values of the enum
I understand the basic concept here and I have utilized enums in my first app. I’m struggling with a concept and maybe I’m missing something. I have an enum structured:
enum USStates: String, CaseIterable, Identifiable {
case Alabama = “Alabama”
case Alaska = “Alaska”
case Arizona = “Arizona”
…
var abbrev: String {
switch self {
case .Alabama :
return “AL”
case .Alaska :
return “AK”
case .Arizona :
return “AZ”
…
default :
return rawValue
}
}
var id: USStates { self }
}
In my struct I’m iterating over all values using:
Picker(“State”, selection: $selectedState, content: {
ForEach(USStates.allCases, content: { state in
Label(state.rawValue, systemImage: “flag.fill”)
})
})
This all works great. I can then grab the State Code using: $0.abbrev in my onChange. Where I am struggling is I have a database value that is a state code. I want the initial value of my selectedContent to be the state associated with that state code. How do I then compare my state value to the USStates.abbrev?
You would need to map from your database value (Which is a String) back to the enum type.
For example adding this function to the enum:
enum USStates: String, CaseIterable, Identifiable {
...
static func generate(from abbrev: String) -> USStates {
if abbrev == "AL" {
return USStates.Alabama
} else if abbrev == "AK" {
return USStates.Alaska
} else {
return .Arizona
}
}
}
and in your view onAppear get the enum and set it to the selectedState.
.onAppear {
self.selectedState.generate(from: "AR")
}