Enumerations are a great, Swift-specific language feature. Due to their extensibility, what is a simple list of options in another language can have extremely useful additional features. Let’s take a look at how we can add a few to the default enum
type: a nice description, a list of all possible values, and a method to choose a random case.
Covering our bases
To add these additional features, we first need to give our enum
an integer base type. Swift enumerations don’t actually require any base type - the compiler handles all that for you if you leave it out, but basing ours on Int
will set us up well.1 Let’s have an enumeration of Santa’s reindeer:
enum Reindeer: Int {
case Dasher, Dancer, Prancer, Vixen, Comet, Cupid, Donner, Blitzen
// and don't forget Rudolph!
case Rudolph
}
println(Reindeer.Rudolph)
> > (Enum Value)
Well, that’s not exactly what we wanted. It would be nice to be able to print these names out, but since we’re already using Int
for the base type of Reindeer
, we can’t set the raw values to be string versions of the case names. Instead, let’s conform to the Printable
protocol2 this way:
extension Reindeer: Printable {
var description: String {
let names = ["Dasher", "Dancer", "Prancer", "Vixen", "Comet", "Cupid", "Donner", "Blitzen", "Rudolph"]
return names[self.rawValue]
}
}
println(Reindeer.Rudolph)
> > Rudolph
Enumerating our enumeration
It would be great if we could list all of Santa’s reindeer, but Swift enumerations don’t support looping over their cases. With another data type, we might extend to conform to SequenceType
or CollectionType
, but with an enumeration that doesn’t make sense – any instance of Reindeer
is a single value of the group; we wouldn’t want to loop over .Prancer
. Instead, let’s add a static allCases
computed property that returns an array of all the cases. First the code, then an explanation:
extension Reindeer {
static var allCases: [Reindeer] {
var cur = 0
return Array(
GeneratorOf<Reindeer> {
return Reindeer(rawValue: cur++)
}
)
}
}
for reindeer in Reindeer.allCases {
println("On \(reindeer.description)!")
}
> > On Dasher!
> > On Dancer!
> > On Prancer!
> > you get the idea
So what’s going on there? This returns an Array
, using the initializer that takes any SequenceType
. That role is filled by a GeneratorOf
that returns each reindeer, starting with a raw value of zero and incrementing until Reindeer(rawValue: cur)
returns nil
. Of course, if this seems like overkill, you can manually provide an array of all the cases instead:
extension Reindeer {
static let allCases: [Reindeer] = [.Dasher, .Dancer, .Prancer, .Vixen, .Comet, .Cupid, .Donner, .Blitzen, .Rudolph]
}
Pick a reindeer, any reindeer
Lastly, since it’s only fair that each reindeer gets a turn, it would be nice to be able to select a random reindeer to lead the pack (except for on foggy Christmas Eves). Here’s how we could do that, first by calculating the number of cases in our enumeration, then generating a random raw value less than the maximum:
extension Reindeer {
static var caseCount: Int {
var max: Int = 0
while let _ = self(rawValue: ++max) {}
return max
}
static func randomCase() -> Reindeer {
// everybody do the Int/UInt32 shuffle!
let randomValue = Int(arc4random_uniform(UInt32(caseCount)))
return self(rawValue: randomValue)!
}
}
let randomReindeer = Reindeer.randomCase()
switch randomReindeer {
case .Rudolph:
println("Shiny nose")
default:
println("Matte-finish nose")
}
Please note: all these examples use the Swift 1.1 syntax for initializing from a raw value. For code compiled with Xcode 6.0, you’ll need to use the older syntax:
// Xcode 6.0
let myReindeer: Reindeer? = Reindeer.fromRawValue(3)
// Xcode 6.1
let myReindeer: Reindeer? = Reindeer(rawValue: 3)
-
Note that all these features will only work enumerations that can have sequential, integer-backed cases - if you’re doing something with associated values or need specific raw values, the implementation would be different. ↩
-
Any day now, playgrounds will start using the
description
property inprintln()
, and then we’ll be glad we did all this hard work. ↩