Swift 4.2: Enumerable Enum

In this article I hope to explain how one can use the new CaseIterable Protocol to enumerate through all cases in your enum and explain some of its limitations.

What is CaseIterable?

It’s a protocol, introduced in Swift 4.2. It automatically synthesises a collection of all the cases in your enum in the order that is defined, however you have to provide your own allCases implementation if your enum contains associated values (more on that later).

Example

Lets look at a delicious example 🍕🤤

enum Pizza: CaseIterable {
case neapolitan
case chicago
case newYork
case sicilian
case greek
case california
}

Conforming our enum to CaseIterable gives us access to the allCases property which automatically generates and provide a collection of all the cases in our Pizza enum. Isn’t this great! We no longer have the overhead of maintaining a list of all cases ourselves instead we are provided with a collection of cases in the order they are declared. You can also take advantage of a pool of functionalities that comes with the Collection protocol so you can perform functions such as map and filter.

CaseIterable can also be conformed to other type even though they are specifically intended for enums, but this require one to maintain the allCases implementation. Unfortunately this is where the limitations begin so lets take a look at those before we adopt this new and exciting protocol.

Limitations

The first limitation is automatic synthesis. Because the compiler automatically synthesise conformance to the CaseIterable protocol, one can only declare the conformance in the same file where the type is defined i.e on the type definition itself or in the same file as an extension. Lets look at some illustrations.

These are allowed 🤩 :

// Pizza.swift

But this is not 😫 :

// Pizza.swift

Associated value is another limitation, automatic synthesis does not work for enums with associated values. This is the case even if the associated value conforms to CaseIterable. This makes sense because adding associated values to an enum makes the number of possibilities in allCases infinite, off course you can provide your own implementation but that would defeats the purpose of this protocol 😂.

Lets looks at an example for the above issue.

enum Pizza {
case neapolitan(NeapolitanTypes)
case chicago
}

As you can see i am providing a custom implementation of the allCases property, although this may seem easy to write but it can lead to many issues due to its maintenance nature. For example if a new case is added to the Pizza enum then the caller of allCases may need to change and the compiler won’t alert us that our allCases implementation is no longer valid. This could result in breaking changes which could be very difficult to debug. One solution to this problem is to recognise all the cases of the Pizza enum through an exhaustive switch which raises errors during compile time. However this is not a perfect solution because you have to remember to fix the custom allCases implementation as well as the compiler error.

Conclusion

In my opinion CaseIterable is a useful protocol to adopt, it provides a useful functionality that you can bolt on to your enums. However there are some limitations to consider before embracing it, as we saw earlier these limitation can easily be overcome but with a small price of maintenance.

Try the CaseIterable protocol and let us know some of its potential applications (while i go and devour some Pizza.neapolitan(.marinara) 😋).

A Software Engineer with a passion for technology. Working as an iOS Developer @BBC

A Software Engineer with a passion for technology. Working as an iOS Developer @BBC