Enums
Enums are frequently used in Swift to create custom data types that have a predefined set of possible values to select from. Enums serve to make code more readable and maintainable, and also provide compile-time checking for parameters and value assignments which yield higher quality, more robust code.
Many languages provide built-in enum features, and Swift's implementation of the enum is very similar to other languages. Swift does have some unique enum features, which we'll cover in this section.
Basic Enum Syntax
Consider the following code, which creates and uses a basic enum:
enum DayOfWeek { case monday, tuesday, wednesday, thursday, friday } var today = DayOfWeek.wednesday if today == .friday { print("Today is Friday") } else { print("Today is not Friday") }
Defining the enum DayOfWeek declares a new data type, which can be used just like any other data type. Because the variable today is of the type DayOfWeek, which can only be assigned one of the seven listed values, we could not assign anything else. For example, the following code would generate a compile-time error, because Saturday is not included in the predefined values:
Var today = DayOfWeek.saturday
The preceding example illustrates the two most important advantages of enums:
Possible values are restricted to a predefined list, making assignment of invalid values something that is tested at compile time rather than at runtime.
Code that uses enums become self-documenting and easier to understand.
Enum with Raw Values
In the preceding enum example, the enum values (.monday, .tuesday, and so on) have no underlying data type. For example, we might want to calculate the day of week by subtracting the ordinal number for the today variable from .monday.
However, with the enum as defined, there is no numeric value associated, so the following code will fail to compile:
var nthDay = today - DayOfWeek.Monday
This code generates the following error:
Binary operator – cannot be applied to two 'DayOfWeek' operands
This is by design, because unlike some languages, a Swift enum need not be mapped to a native data type (and should not be, if there's no reason to do so).
However, Swift enums
can be mapped to any underlying data type. In the following revision, we map the day of week to the
Int
data type, which enables the
nth day of the week calculation mentioned above:
enum DayOfWeek: Int { case monday, tuesday, wednesday, thursday, friday } var today = DayOfWeek.Wednesday // DayOfWeek.wednesday var nthDay = today.rawValue - DayOfWeek.monday.rawValue + 1 // 3 var tomorrow = DayOfWeek(rawValue: today.rawValue + 1) // DayOfWeek.thursday
In this case, all
we needed to do was add a native data type (Int
) to the enum declaration. The Swift compiler then holds a .rawValue property. When an enum has an underlying value, it also becomes possible to create an enum member by passing it to the rawValue: parameter of the enum initializer.
Note
Use care with raw values. Passing a rawValue: to an enum initializer that does not match a defined case within the enum results in the creation of a nil optional.
In the preceding example, we used
Int
as the raw value for the revised DayOfWeek enum. Swift allows any data type to serve as the underlying value of an enum. For example, we could use String instead of
Int
to enable the following use case:
enum DayOfWeek: String { case monday = "Monday" case tuesday = "Tuesday" case wednesday = "Wednesday" case thursday = "Thursday" case friday = "Friday" case saturday = "Saturday" } var today = DayOfWeek.Wednesday // DayOfWeek.wednesday let dayString = today.rawValue // "Wednesday"
In this section, we have looked at enums in detail. We saw its syntax and how to define an enum with raw values. We will now work through an activity where we will use enums to implement error codes.
Activity: Using Swift Enums
Enumerations are a powerful construct available in many programming languages. Enumerations make code more robust and easier for others to understand and maintain.
Use Xcode to define error codes using conventional error number techniques, and alternatives that use Swift enums.
Launch Xcode as before, and create a new playground named Activity D - Using Numeric Types.playground.
Add the following lines of code to create a set of error codes using simple integer values:
// Store an error condition as an integer let success = 0 let ioFailure = 1 let timeoutFailure = 2
Now create the same set of error codes using an enum without a raw value:
// Store an error condition as an enum type enum Result { case success case ioFailure case timeoutFailure }
Finally, create the same set again, this time using an enum with a raw Integer value associated with each result code:
// Store an error condition as an enum type with raw value enum ResultWithRawValue: Int { case success = 0 case ioFailure = 1 case timeoutFailure = 2 }
Now let's use these error values by creating a new variable, assigning the ioFailure error condition to each one:
let error1 = ioFailure let error2 = Result.ioFailure let error3 = ResultWithRawValue.ioFailure
Finally, use the console print function to output the content of each error variable. Note how each one is represented to the console:
// Now print out the error result from each case. print("File access resulted: \(error1)") print("File access resulted: \(error2)") print("File access resulted: \(error3)") print("File access resulted: \(error3.rawValue)")