Swift 4.2 CaseIterable enums and UISegmentedControl, a practical example
On 2014 I wrote a post about how to drive a UISegmentedControl with the new powerful Swift enums. Back then the major complain was that to get all the cases of the enum you had manually list the cases of the enum, which was tiresome and error prone.
This post is a new version of the same idea but updated to Swift 4.2 and the new CaseIterable protocol.
This new protocol has been introduced by the proposal SE-0194. It defines a protocol with a single requirement, a allCases
static variable that returns a collection with all possible values of the type.
But the best thing is that the compiler automatically synthesises the conformance for some simple enum
s.
Drive a UISegmentedControl
Let’s first take a look at how the implementation looked back in 2014.
enum Size: Int, CustomStringConvertible {
case S = 0
case M
case L
case XL
static func allValues() -> [String] {
return [S, M, L, XL].map({$0.description})
}
static func count() -> Int {
return allValues().count
}
public var description: String {
switch self {
case .S:
return "S"
case .M:
return "M"
case .L:
return "L"
case .XL:
return "XL"
}
}
}
Size.count()
Size.allValues()
let segmented = UISegmentedControl(items: Size.allValues())
We can see how we need to implement a custom function and need to conform to CustomStringConvertible
.
Note that the CustomStringConvertible it’s a tradeoff. We had to pick between implementing a custom
rawValue
or custom string.
Now with Swift 4.2 we can ged rid of both custom implementations leaving our modernised enum like this:
enum Size: Int, CaseIterable {
case S = 0
case M
case L
case XL
}
With just this conformance we get access to an automatically generated allCases
variable.
The CustomStringConvertible
can also be removed because Swift now provides better runtime information that allows String interpolation to access the name of the case.
With these improvements we have way less code to maintain and we can still drive our UISegmentedControl
as before.
UISegmentedControl(items: Size.allCases.map({ "\($0)" }))
...
let index = sender.selectedSegmentIndex
Size(rawValue: index)
As usual you can find the code for this post in a Playground.
If you want to know more about CaseIterable
checkout my video on the topic: Swift 4.2 - CaseIterable - Following Swift Evolution