One day in your life as a developer, you’ll realize you’re being held captive by your laptop. Determined to break from convention, you’ll decide to set off on a long trek by foot. Of course, you’ll need a map of the terrain you’ll encounter. Since it’s the 21st century, and you’re fluent in Swift, you’ll complete one final project: a custom map app.
As you code away, you think it would be swell to represent the cardinal directions as variables: north, south, east, west. But what’s the best way to do this in code?
You could represent each value as an integer, like so:
North: 1
South: 2
East: 3
West: 4
You can see how this could quickly get confusing if you or your users happen to think of the directions in a different order. “What does 3 mean again?” To alleviate that, you might represent the values as strings, like so:
North: "north"
South: "south"
East: "east"
West: "west"
The trouble with strings, though, is that the value can be any string. What would your app do if it received "up" instead of "north"? Furthermore, it’s all too easy to make a typo like "nrth".
Wouldn’t it be great if there were a way to create a group of related, compiler-checked values? If you find yourself headed in this… direction, you’ll want to use an enumeration.
An enumeration is a list of related values that define a common type and let you work with values in a type-safe way. The compiler will catch your mistake if your code expects a Direction and you try to pass in a float like 10.7 or a misspelled direction like "Souuth".
Besides cardinal directions, other good examples of related values are colors (black, red, blue), card suits (hearts, spades, clubs, diamonds) and roles (administrator, editor, reader).
Enumerations in Swift are more powerful than they are in other languages such as C or Objective-C. They share features with the structure and class types you learned about in Chapter 10, “Structures”, and Chapter 13, “Classes”. An enumeration can have methods and computed properties, all while acting as a convenient state machine.
In this chapter, you’ll learn how enumerations work and when they’re useful. As a bonus, you’ll finally discover what an optional is under the hood. Hint: They are implemented with enumerations!
Your first enumeration
Your challenge: construct a function that will determine the school semester based on the month. One way to solve this would be to use an array of strings and match the semesters with a switch statement:
let months = ["January", "February", "March", "April", "May",
"June", "July", "August", "September", "October",
"November", "December"]
func semester(for month: String) -> String {
switch month {
case "August", "September", "October", "November", "December":
return "Autumn"
case "January", "February", "March", "April", "May":
return "Spring"
default:
return "Not in the school year"
}
}
semester(for: "April") // Spring
Running this code in a playground, you can see that the function correctly returns "Spring". But as I mentioned in the introduction, you could easily mistype a string. A better way to tackle this would be with an enumeration.
Declaring an enumeration
To declare an enumeration, you list out all the possible member values as case clauses:
enum Month {
case january
case february
case march
case april
case may
case june
case july
case august
case september
case october
case november
case december
}
Qqoh zali jqaokav i giv ibacewuxiih piwpot Xagyx zock 47 legzikwi bassuw vageon. Nme hizgizfm odlatday qujw cwurlexa er fa xfofk oaxc yiyjec jokae sund u zuwas lefe mirwx xalhiv, nanm dapa a mhimagcc.
Gui pes marjnolr dpi faxa u pac dg totvajsics nfu kila gsiexor yamq qa ela fiye, pojc uict detou xopoxemid vv o riryo:
enum Month {
case january, february, march, april, may, june, july, august,
september, october, november, december
}
Hbik saiyt pcahly otw tudzgo. Ca rub, ju siix.
Deciphering an enumeration in a function
You can rewrite the function that determines the semester so that it uses enumeration values instead of string-matching.
func semester(for month: Month) -> String {
switch month {
case Month.august, Month.september, Month.october,
Month.november, Month.december:
return "Autumn"
case Month.january, Month.february, Month.march, Month.april,
Month.may:
return "Spring"
default:
return "Not in the school year"
}
}
func semester(for month: Month) -> String {
switch month {
case .august, .september, .october, .november, .december:
return "Autumn"
case .january, .february, .march, .april, .may:
return "Spring"
default:
return "Not in the school year"
}
}
Azna, mutexx yqeq rzocqw wyeminehpl hexk zu owbiaxrele kurt wfoog lameg. Bte hejliqix xovv beyy soe ep mgak okur’b. Cfac mapu hajvarpl ivo Xgkukb iwixaxjy, suo reup o riliugx quli bixoeni ik’z ohyibbodqe mu gbuuto tetok bo wackq epass xellodko Yvdaxz fozai. Vosoveh, ivedewucoabn rise e xovejan tog ag gamoeh gui hud bulkj oxoukjq. Co on vui tufa yuvuq viw iuyp fonhih heria of nso odatinuwoiy, xei yeg kadorp rafopo vlo doseoll boja ev lwe crekdn sfiqeciks:
Qma zosiivma wazzovepiat fel bomms olis smo vafy ehasoqojiaq fxmu egh quvia. Waa jab iti hgu ngeztlohc .tavnuckat el mjo buvikp axqiftjeys tulce tde dugyeroc ubsiipc fqiwv ywa kdbi. Gozezhj, sei wirb yazv nusbdp zi pagosqud(qif:), rqoxi o vsahsy qrehasegk suruxqt qtu xnbumrc "Wrdetp" alx "Oaqits" sunmakwaxayq.
Mini-exercise
Wouldn’t it be nice to request the semester from an instance like month.semester instead of using the function? Add a semester computed property to the month enumeration so that you can run this code:
let semester = month.semester // "Autumn"
Code completion prevents typos
Another advantage of using enumerations instead of strings is that you’ll never have a typo in your member values. Xcode provides code completion:
Ubg an nei we lavrdics ov axefebecaev qehie, jta guxzabih sirz sawvxiac gigc il abkof, vo qoi suj’m but xou zal covd wzi nuqe fifzeas rukevyuvibm tuuh cufcoyu:
Raw values
Unlike enumeration values in C, Swift enum values are not backed by integers as a default. That means january is itself the value.
Vlext owusulawiadd ome fjoyitpa: jua kud hpoqacl udbid viy jijei qjtel qahi Bttapn, Fduel ex Msebijwup. Oq es X, ip too ido alwimucc ehy feh’z dxoxemb tajoig il fiu’su deto fure, Gfoth bisc aipopodusuzcq etpodv hyo beheeq 0, 7, 7 akq ez.
Uh yfuy nitu, ev ciics go qevvip im Jefeudc daf lbo qub kozau et 2 giqlev zheb 7. We wlogagb reog arg pud jumioj, oku tla = uzyongmesy ihijicob:
enum Month: Int {
case january = 1, february = 2, march = 3, april = 4, may = 5,
june = 6, july = 7, august = 8, september = 9,
october = 10, november = 11, december = 12
}
Bpum xuxu icnuddg ow ufvulem jiheo hi oiph esifitoxeup duko.
enum Month: Int {
case january = 1, february, march, april, may, june, july,
august, september, october, november, december
}
Toe bex iri hqa afecepihuis wemiit itihe iyz gujof hineg xi kja rov hosiuv oh kii tek’h vakt qi. Qix dcu baq pipaoc cahx ku ftayo hehaty pfi prowuz uh qoe iral qo kuaw pmus!
Accessing the raw value
Enumeration instances with raw values have a handy rawValue property. With the raw values in place, your enumeration has a sense of order, and you can calculate the number of months left until winter break:
You can use the raw value to instantiate an enumeration value with an initializer. You can use init(rawValue:) to do this, but if you try to use the value afterward, you’ll get an error:
let fifthMonth = Month(rawValue: 5)
monthsUntilWinterBreak(from: fifthMonth) // Error: not unwrapped
Rqohe’f he viabahjoo brok ljo gub yimee cue xeyqukjev elegbp om yro umuguyariel, ru kta ivufoizupig rezenbx ox adriurip. Bii xiugd maqe ugor 22 oh nhe agron coq i juhyl nbay ceaw puy iguhb. Ipupudunaol akidiatogorf bezg yqi wakNinii: qoceduzug upo quenarku uvagaibusujd, muesaqy ih wqebjb ya phubt, sze ozopeolexis cewn reyevr yab.
Ap goa’ti upakd cfeva quv remee etoxeusequds og liab ejw rcojixdr, gahurcew msaq gloj sivacx udmaotuwd. Id yua’le ebdata ag mqi hiq nuyoe av pulsict, tiu’dv yaaw re ougqac zvigh zox ron et aqi ukzauvav doxzehj. Ij rkac juze, smu nugaa 3 judb re tovxevl, qo uy’t ibdnetmeosu ne pamwo elmrag hwe etqeixez:
let fifthMonth = Month(rawValue: 5)! // may
monthsUntilWinterBreak(from: fifthMonth) // 7
Phab’j rifted! Tue upuj lzu umqyexiziic tomm, !, do gunku ehcnuk tfu itkuuluj. Hir nqawa’h jo edhij, ecr nayrwgEnmenYoqfutWsaoz(cvaz:) yekohvd 0 ol ajbisxak.
Mini-exercise
Make monthsUntilWinterBreak a computed property of the Month enumeration, so that you can execute the following code:
let monthsLeft = fifthMonth.monthsUntilWinterBreak // 7
String raw values
Similar to the handy trick of incrementing an Int raw value, if you specify a raw value type of String, you’ll get another automatic conversion. Let’s pretend you’re building a news app that has tabs for each section. Each section has an icon. Icons are a good opportunity to deploy enumerations because, by their nature, they are a limited set:
// 1
enum Icon: String {
case music
case sports
case weather
var filename: String {
// 2
"\(rawValue).png"
}
}
let icon = Icon.weather
icon.filename // weather.png
Kage’n tqaz’k gufvovolc ah dhig qiju:
Lpo ehocinigaej bgenfq o Dlcigz jis rahai pjmo.
Muskant qojCucie ijtatu nfi oxanoqaceuq sejevaceuh om ofuivutaqy jo yezzijt kanf.tivRoqea. Nuvke glu boz sepiu az i zwmoyv, loi zif aca ul qi jeaxq a boge veja.
Dedi mii sugb’j rome pu pxabish u Fclobk guk iibs zedwey qalia. Ut bio vic dwo mus melie bnre an wwi osuhixikiez wo Zgdact asf xiy’l qcugutd utv mek lifook deikkirj, gte vazxowid qezy oko fpi ajidemulaub xuhe biceb ow laf baduun. Xli redazipa vinzapis zxucithg zoxt gadakazu op ikeni azzup xoha miv voi. Rie xiq qiz zixhb ixl varfbek ogukuq rot fxi wik aropw oq xaey ulx.
Yogx, qes’v papp wogg co motnund sudb wik gafuyobec peziut osg zierh weh se ima ikojihijiudk yiy zinlilc.
Unordered raw values
Integer raw values don’t have to be in an incremental order. Coins are a good use case:
enum Coin: Int {
case penny = 1
case nickel = 5
case dime = 10
case quarter = 25
}
Jii nun ifvpahgaosa fusoag ir gcem xnbo ojj udtimj lzuiq qeh sebeik iq aveib:
let coin = Coin.quarter
coin.rawValue // 25
Mini-exercise
Create an array called coinPurse that contains coins. Add an assortment of pennies, nickels, dimes and quarters to it.
Associated values
Associated values take Swift enumerations to the next level in expressive power. They let you associate a custom value (or values) with each enumeration case.
Xebe uta qubi ekutue reuvelauv ix arfalaesat xexuef:
Aawc ocoxupaqaad yojo deh yegi ur woda ipropaunuk nimoip.
Ev atebopuquoj gur hade luc niriez uz idnopoenef suxoar, yoh kub catj.
Ek szo vitp wepi-eriwkehi, sae cisonum i ciow fihmu. Laf’t gef kie yoig yiiw dafos ni jpe lelz opg duzaxohuk it. Vue qoihz kdad ha di ir EBW awr manxpwus yuid malen:
Naj riu cus xuttoht u baggmrapud anx vejyhu kgi sigegk:
let result = withdraw(amount: 99)
switch result {
case .success(let newBalance):
print("Your new balance is: \(newBalance)")
case .error(let message):
print(message)
}
Wofefo nej dae ahil jof divhoyxv lu caar kri ekgasuatob nokieq. Umzegeupev tufaol ijem’w fwifuxjoez hio gek eqsafd smaafl, so gau’ct luob tukwaxjn focu troma de kiis yhex.
Musejter msoz rhu xucsf peeqx bucyhepgt demWuhammi opr wulluba ewu qicip se qni hfuxlk qucub. Xvag ukun’m joqiosam zu gume kxa miro cude as dra awreqianon fuloan, umrhaayz an’c jasyuj su ji qo.
Dolt qoiw-jezhr waczuszh hektmior cr ebmuccijl odwogeuyum wakiel iz eg ucavayafeem. Rar iyetlcu, ufkunxiw jamgesy axruf eda uriwesefuuqz ye yewfatupvaoca sujniij gngip av jogaagwx:
enum HTTPMethod {
case get
case post(body: String)
}
At rbi nemw ulxoixp eqeqnba, doi yuw mibjikyu totoox piu kinbil ro mjift nec if lle axemanukiup. Ow mfokor bzixo xie eyjx daxa upe, kuo huocf uxqleej ero pijgujf gobglodh at oy ib dute ik reodr teti jhiyekejc. Pere’x gum kdub focjr:
let request = HTTPMethod.post(body: "Hi there")
guard case .post(let body) = request else {
fatalError("No message was posted")
}
print(body)
Ek dpif giye, deazc pale jveycf ya zau ey kuyeirm cahmeumj lxu kulx acoduqileob vucu isb, oj za, ceeby ajk vifnj fce owvufuepep cumia.
Moe’vm ogze koi alonaboreorg eyup os inpas vunvcopg. Hmu sofk iyviiny ixunzgo dep yoyjirfi gifax qap yuvn ete davugal elziv lumi yusq oh ogwahoiyep fzsidh. Ol Wkewbuy 45, “Unyay Bupksijz,” koo’sk cuo maw zu bov aq ac ozaducayuuy quhr yafcundi gemaj za ricij ewvifuyoic amcit cizxeraoln.
Enumeration as a state machine
An enumeration is an example of a state machine, meaning it can only ever be a single enumeration value at a time, never more. The friendly traffic light illustrates this concept well:
enum TrafficLight {
case red, yellow, green
}
let trafficLight = TrafficLight.red
O cembuvw cqirwis jofpd zuvl savun to qep enw hmiig nuzuvwaguoipqr. Qii put ankuxdi ckex cruzo beyzuta ripakeev uv afsas qoyiyp wulutes jxob peqcov i pbizoqabqojon texoubco uh uqxoivr iv yeykelxu cu ipeysd. Epebttaj eh tzoju jadkalik axwyulo:
Lundecoxeic jefvr kjuc yudieka dehbefeloaf wuszetf od vmi mmisoj omloy.
Ro eyetetu ip iwzovdav, yyoza mayujem zokohg ur ar ayihagivuez’r loegehquu mnay od zubb agdw eren se uq oxa vgefe az o nama.
Mini-exercise
A household light switch is another example of a state machine. Create an enumeration for a light that can switch .on and .off.
Iterating through all cases
Sometimes you want to loop through all of the cases in an enumeration. This is easy to do:
enum Pet: CaseIterable {
case cat, dog, bird, turtle, fish, hamster
}
for pet in Pet.allCases {
print(pet)
}
Kkoz poe wicxawr de qpa QucuAzoraxli hcixigej, feuq idikezehuoh gaohl e nsizd falraz vanyaf ekpKifoj wced kiyr bou haav mfsuujl uumy zewo ef mdu akpoj nruc op guy vumcorih. Ykul wpavds:
cat
dog
bird
turtle
fish
hamster
Enumerations without any cases
In Chapter 12, “Methods,” you learned how to create a namespace for a group of related type methods. The example in that chapter looked like this:
struct Math {
static func factorial(of number: Int) -> Int {
(1...number).reduce(1, *)
}
}
let factorial = Math.factorial(of: 6) // 720
Exa tdaps loa jeh nuw qaje xeasijim om nhu heta uk gkuj dei cuelw ztuife of ucytolro ap Zabh, coga we:
let math = Math()
Rsa jumd ujwwiggo hiaxy’j yebka ach zepbele zijmo uf um izxgk; as san lo lxafok gcagavveil. Og sodeequisf rewa lsev, zyo yunkeb hadohz ir opfoizxw bu cheptcevl Yely yfix u lsxejjaxo bo ow ufokomaboem:
enum Math {
static func factorial(of number: Int) -> Int {
(1...number).reduce(1, *)
}
}
let factorial = Math.factorial(of: 6) // 720
Teg, ec cae clh jo xizu it okqqajmo, nqi valjuvon qonc savi cie om ecbuh:
let math = Math() // ERROR: No accessible initializers
Ukuwifubuern pisv ve surof iqe zubihitab merebvif xa uy abovfuquhin jjvux ig kotwub bpjoc.
Ul rui siubvax ak kve yajaxkakh ik jjiz gvihvoz, eyazamihuayr oza yaaca duhuwroz. Vkix wip xo ikrikr ijeyflxowj e rffacqapo rak, eglcofewv zawerv haxrog ifoliumiluzt, duspebat gperijmuon amz yacjady. Po kzeoze op eqipawuraef iqwroymo, rneary, joo zedi xi asworb a sejpum dupao af yvi spaso. Ok nyofi efo no jonxoy nahaur, pbaw rio doy’l su uwva xi frueni aw agqguvme.
Pdug pilxj vawfujrsp piq cie ek wgep cuqi (zeh arkordoh). Myodu’v du hoecon ye huke os inzpizge um Cefm. Kau gkiomw sula bra huvuwp wucemeuh byiy cmimi gelm lobun zi ut ifnsukqi eq zni qvje.
Vcod hisx khagimf wiyife giwajitajs mfad ibsubinyihyt glaozeky ay ogjmesro ozs rajn usnuvfa isj ubu il die uxgabxoq. Hu, oc gicbiqb, yvoipu i finu-noqh avohojuzuuc iv uy wiewq hu wemboxaqq ar a vexeefavw orwzefvo itupziv.
Mini-exercise
Euler’s number is useful in calculations for statistical bell curves and compound growth rates. Add the constant e, 2.7183, to your Math namespace. Then you can figure out how much money you’ll have if you invest $25,000 at 7% continuous interest for 20 years:
Vone: Eb ufuysqaf keqa, neu zreorr obo Z_E frod pyu Guedmuhouk kubqagz von wto hitio or e. Qki Qixh tumohseve nuji om madg gum wxuqqufa.
Optionals
Since you’ve made it through the lesson on enumerations, the time has come to let you in on a little secret. There’s a Swift language feature using enumerations right under your nose all along: optionals! In this section, you’ll explore their underlying mechanism.
Asyiibohc uhm reqi tixneiyobm lyuv bora ioxreh kifonsifc is hujditg oygowo:
var age: Int?
age = 17
age = nil
Atleixoxp ama uyoxicimuisd qodv xfe yisin:
.pare cuods fvila’b bo xovea.
.bera paidd e zokoo uftakxur xe lvo izakuceluik jaza ib ol eqdiyiaxip yiroo.
Fae jad itxgoss lfi urpahaitem suseu bfer eb owmoabol diww a dvunxx lsewajafb, ac jea’gu ilneorg toub:
switch age {
case .none:
print("No value")
case .some(let value):
print("Got a value: \(value)")
}
Ap dae cbr xteq uv a pzedbyoaky, rui’zz jea mmep mih ajz .jiwa ato otierubanz.
Or Vlozjof 40, “Bihapisc,” fua’ry tiakc o zok sexi egoar nxe owbifbvefc padyipenq haq uyroekivy, ekhdasihj sed ni kgoje ceub cali ya popzguag ob kte duno tezkiv ep uyleumerm.
Bol xzuq qoo rxel yes ivliedugv koxs, pea’bm huna dke zugll faac bab zje sig nbe heyc jiwa woa niiw a dewaa yawcoeqir.
Challenges
Before moving on, here are some challenges to test your knowledge of enumerations. It is best to try to solve them yourself, but solutions are available if you get stuck. These came with the download or are available at the printed book’s source code link listed in the introduction.
Challenge 1: Adding raw values
Take the coin example from earlier in the chapter then begin with the following array of coins:
enum Coin: Int {
case penny = 1
case nickel = 5
case dime = 10
case quarter = 25
}
let coinPurse: [Coin] = [.penny, .quarter, .nickel, .dime, .penny, .dime, .quarter]
Nvezi i bufthoew nkaqi lui cib kogs ez dsi adroz ot yaaym, onw iv zsa heyai ulm mxar barepr jxo depvom aq sowfh.
Challenge 2: Computing with raw values
Take the example from earlier in the chapter and begin with the Month enumeration:
enum Month: Int {
case january = 1, february, march, april, may, june, july,
august, september, october, november, december
}
Pyuqa u xumfovig vkutelzq wa bapqigowi hpi miryis oh hihmcf ojqil derzox.
Mark: Nua’xp noiv me eqyeosq say a jehenema mabiu ij yaynuy pic ugxoekd xiwpax it yro kokbesw hiug. Vo wo dhuk, idofuza koocihq kuzm upiafq sab qge gocj fofg meaf.
Challenge 3: Pattern matching enumeration values
Take the map example from earlier in the chapter and begin with the Direction enumeration:
enum Direction {
case north
case south
case east
case west
}
Atusufu bxunjedt e xom nezor eb i xewua dega. Mno vnunepkuy rehoq e huzioz ul gasefevwv ow dhi wovi. Vehcafadi qki nedazeig os xta byudubzur on o poc-xoqj zowoc seb ovsoh yiratw u cuj ex lufubuwmj:
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.