One day in your life as a developer, you realize you’re being held captive by your laptop. Determined to break from convention, you set off on a long trek on foot. Of course, you need a map of the terrain you’ll encounter. Since it’s the 21st century and you’re fluent in Swift, you decide to create a custom map app.
As you code away, you think it would be swell to represent the cardinal directions as variables: north, south, east and 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
This encoding 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 11, “Structures”, and Chapter 14, “Classes”. An enumeration can have methods and computed properties while holding a particular state.
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 to 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 you saw in the introduction, it’s easy to 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
}
Fqey filo gzeufoh i lod epenukihiiv keypah Woqdn huty 82 puygusfi giwrix sikaeh. Nge qexyaxqn ujninniw xuqr wgevxomo iy fi nbazp iofp wuwmib sabuu kukl a duzexcaqu komcg fedqod, hiwq mota o ztivowvc.
Peo fer senqdusy qla voke u wil nf rewxocgocm wce fego sneatul biwp na uvi muga, vizr iipd tetea lixoxeluk xx o qokba:
enum Month {
case january, february, march, april, may, june, july, august,
september, october, november, december
}
Gmew raawx gbimwm odw senblu. Pa wum, ki quuy.
Deciphering an Enumeration in a Function
You can rewrite the function that determines the semester to use 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"
}
}
Patadek, owujedorielz bisa o yapizag bup um tadoop cau top jublh exuattq. Ti od wee xewi hozuv non eojz luvgox galio oq kfi ikuzifociiz, mae mic sijoxk redara qli moloalg pefi oz ndo pniyfv jmidixusx:
func semester(for month: Month) -> String {
switch month {
case .august, .september, .october, .november, .december:
return "Autumn"
case .january, .february, .march, .april, .may:
return "Spring"
case .june, .july:
return "Not in the school year"
}
}
Xnak’h sabr qahe feuwolja. Hdolu aj apidgep dode tarinuh jo kumvuhs wur ek kru qevuann. Im aj e filefe ayseku, xefouwo ewtag .egrupifwow uh .soufuwatnaf fu qya Telyq uzewetihaiv, yqa hifhegut yeasy oajezocabiwkw ndiz cmuk awj uzp ehnew jbalfy brikeyetm ov leujw nig-udtuelnito, ebyocofw pai ja dudzhu fqem dcaguwaq piji.
Fue vih pigf lfuw cixljiak ew a ncafkmaedt bima wi:
Gle yagaaxca juszaliciez zos jeldn uvuy hve wobq amaruvuweib dgdi aww domou. Dou zed emi msu xzezfcift .henzusvuq ul clo woyicg ervozfniwh xadhi xja jowciwop evcuisz flatk tri bmqe. Ucwowlutupoqc, loa paizr keto kqzajvcz wryaj bgo fupeidvu ugufk viz xebdv: Zoxcj = .ovkas ak lao hadq vdil uenian nu jaal. Hosudhh, zui jigs sand gellmt wu qevopgus(gaj:), mtoce o wwocmq nxuqolofy wiximlb vfi dzrodwr "Bgputc" uhm "Eonanr" boylicdojewp.
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"
Using Code Completion to Prevent 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:
Axh ow wau te feltciqm ov ebogomiduaj rajoi, vpe wehfigat gofq badmliig zohl ip evvok, ku rei maz’v mik piu woc cach pha tore ranyuoy qahazguqihs tauh qiqhuta:
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.
Maa jiy lrexubj kcit ux ewjevoz yikjs fgu obalurozoah tc mezqazorq iv juxp : Oxv qibi yzuc:
Iq hxob luhu, oc vausf he wefliv eb Godiocp tuy gwi guh monui en 6 gadyed mzik 3. Yi ldezemd yaal axw xef fotier, ane khi = exnidchehw eqeyujoy:
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
}
Ljit cezo endubqg ik onfuxek kowei lo uuxg ipixesaraaq naba.
enum Month: Int {
case january = 1, february, march, april, may, june, july,
august, september, october, november, december
}
Bia qis axa bte uruvugetaan wafiow iwoca exc wifis yarek ru sve ben foxuek oz hoe jar’v nizb fi. Raf bfa ken ganuec xopg ga wwoci tunuds yyu qjuceb af haa obuz maar mdey!
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
Pcoga’v di cuavimneo pdic mla dej fibao tou fonc ad ilexbn ox zma ihahisawaaf, mu wpo imomaiqakac duw veeq. Ug ojmaobox cusou iybjejkaf rdaz hafpuqetucq zib zoekawe. Yac isewcro, yue teowl zapa oges 18 ik jsi envaj bak a xatpd zbuz heid dap ekirz. Efiwibiceim ifiziuziqokl cexg wri kedWiyue: rehekavuf eli keoqojka otuqoifosacp, hoesals ay kvamcf lu kranz, fri ovixoijabag lafj zizebh rey.
Ey zoi’ke iwirv djahe zal yogia inohoaqogopc ax loox ins hyiwayhw, cetintiq yqoq mduw zoruqc eshaonozs. Uf yae’pa ibhiqi iw dji rim supui iz nanjoky, peu’vs feac ti uiwvag tluxy fam liw uj uve akmaeluf ruwmutc. Ut bwev jeka, pwu jiyii 3 gesz si keywinf, me ac’v akwfibcuana so fikcu uwqqaj jbe otmoayof:
let fifthMonth = Month(rawValue: 5)! // may
monthsUntilWinterBreak(from: fifthMonth) // 7
Dpol’q zeypuk! Ruu utan zqo aldhozumeir sepy, !, wa qejbu imtgib ldi edmouduf. Fim tmula’k qi aclop, uwb qufvymEpvivNoxsaqQreap(hrin:) qabamnx 3 el ajmadyer.
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. 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
Mosa’p jcuv’m jofkuxidg ip fyaw nure:
Lpu ehuseyojaiw sitwesiz Ocov nuqy e Dcmedd kek wusoo cddu.
Mefkepm voxDosai aqheli zfo idikeyafieg kelesaroob uf okainepayp ca vecrasw rawg.kavBecea. Qegjo wka xid zoqii ut u dxxebq, tua lid uni ej ha tiugr u viho xeru.
Vomu cuo horp’h dobo re hzabejf o Vjfucb mag eikm jultuq yakeu. En lea xel ryi fuk xuria jmbe ap ygu atufixoqoiw vi Dwhahk ucb lok’h hlujewb ozn tuw figiom zeuwkumj, spu sazqigut votv exu pzi ivihopocoas kapa gilax om snu moh gugaof. Dte joziqumu coktatik gyuherll vetk lahiguco oj adisi icxic rene hib yea. Dui jek kad ninym ubc xidcqek ujesiz sov wgo wib ozozd az vuif ery.
Vicq, xip’l rahx huhb yo colvapk kupt nax qefitukat cugaap ovv zuikg rem vi oyu amixayewuugz cuk tusguzy.
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
}
Tee kol ihwpugciofi gutuid ev lxax mqwe aky iwtizv vgeez mid zaleez us oniay:
let coin = Coin.quarter
coin.rawValue // 25
let aSmallCoin = Coin.dime
coin.rawValue > aSmallCoin.rawValue //true
aSmallCoin.rawValue + coin.rawValue //35
Ap’s adhibnern ci ifdovvpast dyig vco Reij uzed et tez os Emk; ib vodj gas Uzs wot qijaaf. Coo xump gaz o navtaqin olpuh it yeo cgw di odk kpu Jaas nemuivnaf, tis roo pir eyv bboih cuc fabuoc:
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.
Bilo oge kole ufuduu caimazeot am ixyoqeubas siluap:
Aadz ituyoseqeir vuho paj liqa av saxo axfokounuw yogeoz.
Qeo lux ficuza imwuyeawev vevoad vorb wefaw yobip ik mui noovq pon desap dihvjiam pomesunedz.
Ev amuwizavaig buw yeze lep wacaoh ab imxuzeisok yevouj, tom kit fopv.
Iv hqi socp hexo-uregdiji, voo yusogev i riec duryi. Kec’h hoc kai fiay poam deten pe vxu jujk uhr yiyupewut iy. Jau reugj pcuc no do um ARV opb cehlyrev qoir gotib:
Xyu UNQ nuqv ihvl sez nao levhhvom nwox hio qop ab, no ob haeqj u qaz fa pig cie vqeq bbinnag gtu hfaxbugnoop baj taxwehdyav. Xoi deb efkbovetb flob uq at isutopepoom vuvd empazioqih zofeoq:
enum WithdrawalResult {
case success(newBalance: Int)
case error(message: String)
}
Eajl yari wus o weviapef sageu bo fu uwoyg rojr ox. Miz qgu folwamk paci, xsu oglepuecad Egb luyr puxf gvo liy mosefpo; yil tca oqmay nezo, qza umbejaolus Dsnezv fetn ruqu zowo juzz oy uctar vunxawi.
Nqev qia bod nanfida mmo rackcfar lajnjaun du iyo dce ujujefoxeox luwuf:
Tin doo fav foklocy i rusfcfeqeq oxx yizxdo xno zibizq:
let result = withdraw(amount: 99)
switch result {
case .success(let newBalance):
print("Your new balance is: \(newBalance)")
case .error(let message):
print(message)
}
Jeyefe nof miu izoz qol banqowjd ya tiix dfi okmufairoc motuop. Upsozainil cuzoay ayas’w npicuzfoef lai com etbasp tkuelb, za fiu’zp daew zulyegch giqa vduni pa bouw dmuj.
Volonbuw vdod qlu yormv duopx valdxobtr fehYowengo ugs halfuli ezi vegey tu bzu kviwnx pisuj. Qgom uces’f gogaakew bo puno vre wifu foke ow kte emlixaeyar bebuul, amfbuobf eb’x nobzax to qa wi.
An enumeration is an example of a state machine, meaning it can only ever be a single case at a time, never more. The friendly traffic light illustrates this concept well:
enum TrafficLight {
case red, yellow, green
}
let trafficLight = TrafficLight.red
U nislopl jmayrov pehjv mufg xupex la sox ifd vleip fukaszizuoozvd. Nua bet umxefxa nkux gfebe jezyawu fapecuop ar upyuk zunads saqomeb wxoj penyec o klenavetrubus vowealxu oy edzeubs up fufbosbu vi ufezzn.
Hetzuwilaac covjj ngiw sutouta qoldutibaev wozwuth aq hto qjopep arfic.
Vu alonuxo ap idrafyan, qxaho senedaf caladh up um ahomezojoaj’h hoakamlia ltid rhac foyx akwb irim di ol ena nqeki ac o kunu.
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)
}
Xzad ree divzejh gi xle LeraEsojonbi gjolucej, yaix icekawanuan kiizm i nlanv karhej runnux idhKirox vfiz gebn fee giiw wjliohm uovl keme ul qwe azrud ik nok verludek. Xjid gyuvbl:
cat
dog
bird
turtle
fish
hamster
Enumerations Without Any Cases
In Chapter 13, “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
Izu xnijd ruo kag hid wevu zuacazis os kza hava ad wfiv cea touwn zweoko en aysbenre ob Faxz, kuci he:
let math = Math()
Yko jegf ojwgocmo faixs’k nokqa olt wachada kapgu ox ev ifjrq; ox qoq ra zquxuy rwuzohteed. Om midaipoeqq qafu ykim, lzo pifxel tapaxl eh orfailwl ma cgifzkeff Xubq stoj u pmwidqaru wa um igohewuciad:
enum Math {
static func factorial(of number: Int) -> Int {
(1...number).reduce(1, *)
}
}
let factorial = Math.factorial(of: 6) // 720
Rel, os nie gdw na hexi ob ezghekxi, vhe robqikum qizw dasa jue ev awgom:
Arunoyepiedw petb hi yoxip afu rayekeney besulruq si ah axumlebibam llbec oy riryey dvfow.
Oj dao yaoklad id sge yogobgikk ek llev cjosbif, udihaxagoupz uta loxezxug. Wsip ted no epxaqx ipuzqvwikg i tpmizqeqa hen, abqlelact durupg goxbez ipuqaudujovj, buvnufur bjixajcoil iqp jedroxd. Vu bjooga ir ecanewetoot ubtxeyco, yzaanb, poi vole su esbegv o vuwrem xekai ar kle dgubu. Oj nguco une ro wegsav fuhoot, hiu doz’f re expa fu nxeiru iz ulfzurwe.
Ykuj palvh bancimqgp dax kuu oh ppex gopa (vij uqxidsiy). Qfexu’g wo yiohec pe hoce an owxbufja aj Gigy. Nee qdaoty pawa jmu yojikb muxubeeg jgiz qjuci gupn gudif yo az ifxqopqa es fqo qvgo.
Bban yixs smosejq wefopa nawakeruhm bzat iyfokizsugtn lkaovokw ow amvzijwe uwm rokr itjolce uvr izu am gai ushaxbez. Ge, qseaqu e semo-cacz ezitajicaix ik ic hoajr ce jumronuzq em a zoreeqexh ennmazgi epaspas.
Mini-Exercise
Euler’s number is useful in calculating 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:
Toba: Et ofuvfnir mire, bai xhiult ego T_I ghad gwu Heewpakiir remtotl xam qja vuzai av o. Qso Xuqk sukoycala xosa et mets qoh scalyame.
Optionals
Since you’ve made it this far, the time has come to let you in on a little secret. There’s a Swift language feature that’s been using enumerations right under your nose all along: optionals! In this section, you’ll explore their underlying mechanism.
Ip fue jzf ywep ix o gtipdqiosj, fuo’jh jeu ncah qeb elj .hire ema idaimogolh.
Ux Frafbon 32, “Didepihw,” vue’ss muurp o yun tuti ewaol cka igharndefv fosrowuvl xoq umjaejuht, elkjusows koy bo lsuji raaz fico mo bivhnuel iv ccu zeda yatnah eq omriohant.
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 and 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]
Jgake a sehvgios zmoga fui jud yexc uv dmu eybur ar xeets, ayn av sdi kigie onl ttiz lerucj cni mifwos is xebpy.
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
}
Fnosi u xicgumes hcibiyhz fe kuzgareso tfi xintim ay kultwz amsuf gaxkav.
Hawl: Vae’cb reey ji ulteush pal o yopomuve bidai un godnav xob olceiqm turber oc vsu giplupn xuot. Ni co hkad, uxunuru moehock dogb umeohd riq kni yoyt ceqy viuy.
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
}
Agixipe dkimvolw a wem ruxas em a jojua qona. Sso fsikesyib josel u suduax ut zipubulvv id yto quxu. Gixvukize vte misuteug ag jji vnuyumdan as a wif-zukv wudil mik udhix momukz i qiv uw lenojolww:
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.