A little known fact* is that the inspiration behind the 1987 chart topper “Never Gonna Give You Up” came in the form of assembly code delivered to Rick Astley while in a feverish dream state. The prevailing message of this lucid epiphany came as an enumeration:
*possibly made up
enum ThingsRickAstleyWontDo { case GiveYouUp case LetYouDown case RunAround case DesertYou } // Implementation // assign the type ThingsRickAstleyWontDo to the variable var heIsNeverGoingTo = ThingsRickAstleyWontDo.GiveYouUp heIsNeverGoingTo = .LetYouDown heIsNeverGoingTo = .RunAround heIsNeverGoingTo = .DesertYou
Out of the box Swift comes with a collection of built in data types for your declarative pleasure; Int, String, Double, Array etc… but Swift also allows you to define your own custom data types.
Enums are a custom data type consisting of a finite set of named values (members) which may themselves have values (associated values). Any time you’re modeling something with a finite set of outcomes the enum bell should start dinging. Examples include: coin toss, dice roll, days of the week, number of planets, choice of e.coli riddled meat at Chipotle – all great candidates for enums.
Raw Values > Raw Food Diet
Another wonderful feature is the ability to access raw values – underlying data types assigned to each member. Note these will all be of the same type (so all integers, or all strings etc…).
enum CelloString: Int { case CString = 1 case GString case DString case AString } var string = CelloString(rawValue: 3) // = .Dstring // Or var anotherString = CelloString.AString.rawValue // = 4 // can also retrieve raw value: string?.rawValue // = Optional(3)
Note that because not all raw values have associated members the type of string is optional CelloString? not CelloString
Associated Values
Beyond raw values every member of your enum can have multiple associated values, which can be of different types for each member. So let make some beats!
enum DrumPart { case KickDrum (brand: DrumBrand, size: Int) case SnareDrum (brand: DrumBrand, size: Int) case HiHats (brand: CymbalBrand, size: Int) case Cymbal (brand: CymbalBrand, cymbalType: CymbalType, size: Int) case Tom (brand: DrumBrand, size: Int) } enum CymbalBrand { case Zildjian case Sabian case Paiste } enum CymbalType { case Crash case Ride case China case Splash } enum DrumBrand { case DW case Pearl case Yamaha } let myKick = DrumPart.KickDrum(brand: .Pearl, size: 22) let mySnare = DrumPart.SnareDrum(brand: .Pearl, size: 14) let myHats = DrumPart.HiHats(brand: .Sabian, size: 16) let myFloorTom = DrumPart.Tom(brand: .Pearl, size: 16) let myDrumKit: [DrumPart] = [myKick, mySnare, myHats, myFloorTom] // an array of custom type DrumPart
This model might lend itself to an app where the user’s creating a virtual drum kit from a finite choice of parts.
Methods Inside Enums
Enums can be further juiced up by adding methods to them. The example below shows a way of organizing the possible values (in Hertz) used as the tuning reference in a music app.
enum TuningReferenceHz: Int { case Baroque = 415 case Standard = 440 case NYPhil = 442 case ViennaPhil = 443 case BerlinPhil = 445 func description() -> String { switch self { case .Baroque: return "\(self) tuning was more commonly used in the 17th century" case .Standard: return "\(self) tuning was adopted as the standard in western music in the early 19th century" case .NYPhil: return "The New York Philharmonic tunes to \(self.rawValue)Hz" case .ViennaPhil: return "The Vienna Philharmonic tunes to \(self.rawValue)Hz" case .BerlinPhil: return "The Berlin Philharmonic tunes to \(self.rawValue)Hz" } } } // use these values elsewhere: let myAppTuningPitch = TuningReferenceHz.Baroque.rawValue // tuning now set to A = 415Hz // give the user a brief description of the tuning standard let descriptionLabel = TuningReferenceHz.BerlinPhil.description()
Safety First!
Writing “stringly” typed code is a quick way to get approved for a mortgage in Crashville.
“Safety” is term frequently associated with Swift. Enums are a great example of how you can make your code safer. In the above example the description method uses a switch statement to return a string. Because it’s switching on an enum (self) the compiler wont let you sleep in your nice warm bed at night until you’ve accounted for all of enumeritastical* possibilities. This also leads to more maintainable code. For example, let’s say further down the road you’d like to add in other tuning standards to the app. There’s now a central place to make this change plus if you’ve structured the rest of code nicely you’ll get a warm cozy blankey of compiler checks before you release an update that takes someone’s eye out**.
* possibly not real word
** worst case scenario?
In Summary
- Enums are custom data types
- They are ideal for modeling situations with finite outcomes / possible values
- Great for use within switch statements as the compiler will check that the switch is exhaustive and prevent error resulting from stringly typed code
- Each enum member has a raw value (pre-populated) / can have associated values
- Their functionality can be extended by adding method inside
Find a playground containing these examples on GitHub here.