Enums are a vital programming tool in Swift, as they allow to define types that enumerate related values and therefore write code that’s safe and specific. As we all know, enum cases can be either simple, or with associated values that store additional data tied to them. Regardless, it’s often necessary to list all cases or iterate through them for numerous reasons. Even though the obvious way to do that would probably seem to create an array with all available cases manually, there’s a much better approach to achieve that; to let the Swift compiler synthesize that array automatically for us simply by adopting the CaseIterable
protocol.
Using CaseIterable
Every time an enum conforms to the CaseIterable
protocol, a static property named allCases
is made instantly available to us. It is an array containing all enum’s cases, so we can operate on them as we would be doing on the elements of any array.
For instance, see the following enum that conforms to CaseIterable
, the cases of which represent weekdays:
1 2 3 4 5 |
enum Weekdays: CaseIterable { case monday, tuesday, wednesday, thursday, friday, saturday, sunday } |
We can get all cases as an array simply by accessing the allCases
property through the Weekdays
type like so:
1 2 3 |
Weekdays.allCases |
As it’s already said, allCases
is an array, so we can use any related API necessary for our cause. We can, for example, just count the number of cases in the enum:
1 2 3 4 5 6 |
print(“The week has \(Weekdays.allCases.count) days in total.”) // It prints: // The week has 7 days in total. |
Or, as it most usually happens, we can use allCases
in loops, accessing its elements one by one:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
print(“Week days are:”) for day in Weekdays.allCases { let capitalizedDay = capitalizeFirst(“\(day)“) print(capitalizedDay) } // It prints: // Week days are: // Monday // Tuesday // Wednesday // Thursday // Friday // Saturday // Sunday |
We can write the above much shorter using the forEach
higher order function:
1 2 3 4 |
print(“Week days are:”) Weekdays.allCases.forEach({ print(capitalizeFirst(“\($0)“)) }) |
Note that the capitalizeFirst(_:)
method used in these two examples is a custom method that capitalizes the first character of a string:
1 2 3 4 5 6 7 |
func capitalizeFirst(_ value: String) -> String { guard value.count > 0 else { return value } let firstLetter = String(value.first!).uppercased() return firstLetter + String(value.dropFirst()) } |
CaseIterable and cases with associated values
Things get just a bit more complicated when having cases with associated values. In such occurrences, we can still adopt the CaseIterable
protocol. However, the allCases
property is not synthesized automatically any more; we have to manually (and mandatorily) implement it in the enum, and return the array with all cases from it.
To get a taste, see the following enum:
1 2 3 4 5 6 7 |
enum ComputerOS: CaseIterable { case macOS(version: Double) case windows(version: Int) case linux(distribution: String, version: Double) } |
The associated values given to the above sample cases regard the versions of the operating systems. Leaving the above as it is will force Xcode to show the next error:
“Type ‘ComputerOS’ does not conform to protocol ‘CaseIterable’”
That happens because the allCases
property cannot be synthesized by the compiler; the above cases have associated values. So, let’s add it manually to the enum just like it’s shown next:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
enum ComputerOS: CaseIterable { … static var allCases: [ComputerOS] { return [ .macOS(version: 12.4), .windows(version: 10), .linux(distribution: “Ubuntu”, version: 22.04) ] } } |
For each case we provide an explicit associated value, and all of them are contained into an array which is returned from allCases
in turn.
We can now operate on the enum’s cases as necessary:
1 2 3 4 5 6 7 8 |
ComputerOS.allCases.forEach{ print($0) } // It prints: // macOS(version: 12.4) // windows(version: 10) // linux(distribution: “Ubuntu”, version: 22.04) |
Note that since cases can take various values, we can include in the above array as many cases as we need along with their respective associated values. For example:
1 2 3 4 5 6 7 8 9 10 11 |
static var allCases: [ComputerOS] { return [ .macOS(version: 12.4), .macOS(version: 11.5), .windows(version: 10), .windows(version: 11), .linux(distribution: “Ubuntu”, version: 22.04) ] } |
Lastly, keep in mind that an enum can contain a combination of cases with and without associated values. When that happens, it’s still necessary to implement allCases
and return an array from it manually.
Conclusion
Iterating through enum cases is not a rare task, and it’s easy to manage that as you have seen in this post. If the enum has simple cases only, then doing so is as easy as it gets. Otherwise, it just needs some more work in order to return the array with the available cases, but surely it’s still not a difficult process by any means.
Thank you for reading, take care!