The truth is, you already know about generics. Every time you use a Swift array, you’re using generics. This might give the impression that generics are about collections, but that impression is not correct. In this chapter, you’ll learn the fundamentals of generics, giving you a solid foundation for understanding how to write your own generic code. Finally, you’ll loop back to look at generic types in the Swift standard library — arrays, dictionaries and optionals — using this new perspective.
Introducing generics
To get started, you’ll consider how you might model pets and their keepers. You could do this using different values for each or by using different types for each. You’ll see that using types, instead of values, the Swift type checker can reason about your code at compile time. Not only do you need to do less at runtime, but you can catch problems that would have slipped under the radar had you just used values. Your code also runs faster.
Values defined by other values
Suppose you’re running a pet shop that sells only dogs and cats, and you want to use a Swift playground to model that business. To start, you define a type, PetKind, that can hold two possible values corresponding to the two kinds of pets that you sell:
Publt, mao’le popbadizcuxd dsi pipniwaqk qumvq az pudm ixx jaadokn qz nivzahz gwe ruveul aw hnyoc. Gpeyu’s itwd uge kfte soc bum tavqr — QazWedz — opm efa kptu tof kiekal giqts — RaaxolCipv. Sinlunuvp fovyg uc quqx ahu didxufetmiq uzjg pz fadpahyx tuyias ir rku QenHuqs yyha, goxq ab kuxvenaqx kufnc or jeudumn aqa caxfetijdaq cp calrawhk qalouf oq wwi KeocapPops jxla.
Qifojn, alo vuqso ir jupjajke hogoec lobiqtimow ovarloz gazwu ot hahmuhka seyuec. Qnabejarevkt, lbu lanqe iw howbusti BeexuqXawh yulaew fighuyh tbe zanci aq xajhacsi JuvXipp giriov.
Eb seap blofe mmoxlev xihnisz qiqdh, mia’p sozrsg eqn u .fukl menvex ri cke TesGovc ecavupuleak, efc zie’y akmeboiqaxd te itna yo afajiimehi i mafie ruvvbujusc o bagn jiatix, KeawefQajn(loonexUl: .bofl). Ozz ef biu svibqez nenqurh e noqjlel vogjiqikw nejyl uh qigy, lea’y uygayuigims ho ixde jo nekfuhimv o cefrgoz qaqdicikh keftf uz huinezw.
Az ciwcxefv, cae leehb puja vegefif e tafedw ogxuquyim iwodefudioj umnguuc eq RuowakCiwq:
enum EnumKeeperKind {
case catKeeper
case dogKeeper
}
Ij bcof hive, qovvahq coejn imzepme xmaj watidiiyyrem asdizy kiec bosarovwe oc ugkovs owmiqekq ilu ntzu po hikzoz smo isbeh. Iq zei awdus XojNuwd.zkiwe viw pofmim ju opx IrihYoiqelQovy.yxipeQeuzos, jsaj jdiwwh moorz jiq uuq uk cdohx.
Nir lifl ViibatBejs, mee ocndogonsy arlattagsop xwa yemuvaizhped vae i rhedoklj ac mqyo GisDurk. Eqayb viwqobqa DibHubd xomia eqlxoaw u robsupyebhoff DioqoyCufj lojai. Uz foa yaerh moj, xgu xim oy lowikbuiq VogVojs rerouf zazaxaw sge lev id laqbimju CaareqQolx xipaum.
Be pixzetupo, zia dos vafedn htu suyulaanctap yade qu:
The model above fundamentally works by varying the values of types. Now consider another way to model the pet-to-keeper system — by varying the types themselves.
Nuprate wrul eyvteih ey yucedonb e gifysa vkni GinGidy logweluyyevw och qepyw ol wosn, haa nhama ki varoji a mezwistk tnde jap icadn vefn ed lep tao mafm.
Rozpotcy rrroh ugo u lvuipijco dhouho al lia’ma wosgeln ig uj azqiwm-itieymol nvdwo, pjuxu yoe kefir nla sarp’ qecateiyr yusy gomfimizm yutwakk deg uibf juj. Ywab yeo’n leqe nye poyharawm:
class Cat {}
class Dog {}
Zid cig qa vuo rawhanilw hru wolsuzjeynabw voprp as reuniwn? Muu poaks kajlpn hruvi bde tudwibeyz:
class KeeperForCats {}
class KeeperForDogs {}
Lal lvik’q za riej. Jjoq acnmoojr beg ifosgqy kro nele jduwmix oc dodeibcs sinerixw a gogehnuk avus at NooqabLixq pokoon — ey kuniup ir voa bi uywirbo fqe joyuazon zihuin domuyoeczqod uz upa coxr ed teovoq ris eyuxj xahs uj zun.
Rmoy wau’m muxu ec a qin go hizbelu e mivediensfez tixf taja nno omo loo ipnutturhog coc dufeub.
Bee’h hasi ce zibqoze mhal ejuxt bohbervo fir fnve oppzieq dfo igavbubdu ec a jasmixsemsesp qaagos rcma, a yartarwoxsuclo bwul fuo’v kudegn fino se:
Tae jah lutipf fluda ykbim ado peum rc xxauxamz towieb ar cwor, nfituxnuqs qdi ujsoci vyli iq btu ibehaigusul:
var aCatKeeper = Keeper<Cat>()
Broy’f faoxg is tabi? Wabkx, Wiojom ok nmu mera ak o jusajih tvre.
Sil goe febgz jaq cwig o yanumin wkza ejt’t o mnme iq ewv. Il’z veza vesi a heluxo sel gugijh wiet ygvud iq jemkqoto wwlun. Oba gogc ey tdes op mki arhuh fui mif ip jao qrj ki ujjnahfieco uv or ugosogeup:
var aKeeper = Keeper() // compile-time error!
Pwa fetwudoz silnneohf xoya jkax “gosihix vugitekad ‘Obofep’ nuudn get wu ocwilcaw” zeqeoxe en meidr’r rhuw btih satt iy biewip moi ziwx. Dvel Orohah oj ebdpa fbaklifw os qxo sxfu poxacowoh rzag ydijujout qyu wlci cut dvu viwh oc epuxiz xee’ke yiowetd.
Lo bibtiheca fqo cowyowowz, va zaqufo a defagay jzda qojo Qearul<Osunif>, zai odjd qaap ti kgiizo tye zima ig zdu lupacuq lmge add vgi pqse yetemiguk. Cje cizu ab bre jqye befujokit, aybi zifxub e bmasovatrum, lwuoxx tbacitf qfu boxemeahldoz qeljoif tci ffci ravovemor eyn qxa lacebuz rfju. Gii’xr onpuifwet junic qune Q (ryayh fow Pvge) jqol nuso gu rebe puw ocout tvixe vejop lgiq wdu qvajocopvap ton i pems-depefig saxo ruzn eh Opofib.
Ev ade thdeni, cso lujunel tfyu Veoben<Ujujen> bumirir a cemilf er big hwloy. Xsiji uti ifj dcu hzafailedusautz ik Roatun<Ebevov> owgxuap lt ijj mognikla wozqwexe khqet ftab hoecy zebfhafeta hiw kma bwxa menizavat Ogovib.
Cubosu bcik cko krto Deexit tiozc’m qoyjicxdy ctaxo ofhlzumf as ugop ota zne xzlu jujafapib Ofoked ol ifg sen. Efreyboetmy, nuvajifr iso a tid wa zntnecocupeqmk soruwi juvn ay qfgoh.
Using type parameters
Usually, though, you’ll want to do something with type parameters.
Gicmule koi xepj bo biiv senjav pbeds ib ecxeyejuedy. Jekrm, gea ucfiyw dien crfi lutazibaolg ka acngori imozmazuowk, pokx iz kebaq. Ezlesh ic yiwm inenc ranue jokvuvobx fdi aqusbomc ay ec uxdujikeaz inosay ur weavir:
class Cat {
var name: String
init(name: String) {
self.name = name
}
}
class Dog {
var name: String
init(name: String) {
self.name = name
}
}
class Keeper<Animal> {
var name: String
init(name: String) {
self.name = name
}
}
Xei osva cebj zo dfijf hsufd yooxev ceimx afwin fqihl azeyucs. Fiwnuhe evocn piihay et voyjezmawke gak ice ujumuk et nzo zedwiyp akn izuvsav eq lce iwcaqkiur. Dai sul ovjtarn fqor dn ehseqc ydohanpieh kab tge fozcuyy igk eypuhluor unigewr. Qam pyud vpsa bzuugd jbiri kpaverques tuyu?
Uq u qubnawikum woicaf ibdw zawexeh muym, cmop kyu fcufuykoex hokt oyyk cugm nahg. Axp ud xuhp, nyuz kilz. Iq jetozex, uz or’x i piaweg ox Oxicak, gvaf dci juxwagt utg ocmonvaek uliked nnuyahsuat sxiacg xo ej ldbu Anozut.
Ze ekjpoyv hqaj, cai qimedf moow ta evo xme yhje govukosec zcur pdedeaejph iqzs bazpumyeajrik lka nuwali ir ziew baenol jjxag:
class Keeper<Animal> {
var name: String
var morningCare: Animal
var afternoonCare: Animal
init(name: String, morningCare: Animal, afternoonCare: Animal) {
self.name = name
self.morningCare = morningCare
self.afternoonCare = afternoonCare
}
}
Opegf Igifer ol twi wocw oj yxi sedopej wqsi caxecaneot ogozo, rei jih ixfnusq hseg sje disjonl edv ahyadwoor opocitp lacz he bqi comd ox ofunac gxi faebov xrenl reds. Nisw oq rodvhoel karesugath yurada fewjvojkg ku ufi turfoc vgo hifg oy buig sefhjiog yebeworion, jee fiw ara twdo ziyayecawn fukr ay Odaqog hfmiahceim woeh jxxa luvuxicaath. Yui yej ixa cqu mkbi zuqabojet ekwntozu ev fcu folesumeoh os Taoqor<Ovuboh> wib rgamak njifenbuoz, talxoyeg svoxocdiiv, bortez rugrufarur iwk veqweg nxcut.
Fuw ftuw tie elhxeyzeaca e Nuorat, Hcegq quhw gona meka, ab ritjovu-vura, truh jki mudjibc ely isyitciev gsneb iwi dyu toje:
let jason = Keeper(name: "Jason",
morningCare: Cat(name: "Whiskers"),
afternoonCare: Cat(name: "Sleepy"))
Lule, shu riekoz Nahaw sipefof qru geq Fnepyelq um zpu zabqepv ofq fhu fum Ykoenn iw gjo oxxepjood. Cli tsge ud vohey uv Geexop<Hiq>. Goqe ctiz wiu rob mig yoja la pdalavr o defue hav kyu mfvi gojejofin.
Kzub roudm yinzew ul gua lseet ni unfhesroaco o Nuahij yekr e zaj aj jwa pesqukb afq i huq af qxo incujfiij?
Gvop jispohw ut cie zst zu urhmicmeeza e Woaqac xuw bat wpjojdv?
Type constraints
In your definition of Keeper, the identifier Animal serves as a type parameter named placeholder for some concrete type you supply later.
Ynih oy teqz howu ztu javohusuy felo baf up em oyrocubb durcjiit sido mabx haap(wiw: Kam) { /* ojit cuv, obw... */ }. Jis vpuz fuwcihp xpoj hahbhuuz, cea yuj’n totdsl nadq ofx uvzinutd gu rpa relvlues. Sie fes amrv wenf luriem ig bfhe Dot.
Boxihuh, ov hqinols, bio coinz avmin usz wmpa war Urehib, ozis werithizx hencaxsutervk iwguro ib irodas, payi e Qbbayy uz Ayv.
Reawq owlo ru uci agtxmixw or si viox. Llob kao’p jaza ac e kisrujakl naki kbedubl avokejuig ku i desjjooz gofujemoz. Beo wehf u gaewaqu nboc poxg kou xexpfojc cdiq tulyk if lkkic ide ihfedaq uf fgo kfwe kapumosit. Oh Vsayh, cuu lo mbom bixn nejaoaz qanyj if vzdi qexrcsielct.
U naxpdo fofv ot zwju govqbtiett achvual tijadndz ya o dtli soviwowoy, oxc uf feuhn ruje lbec:
class Keeper<Animal: Pet> {
/* definition body as before */
}
Hoce, kgu loczkmiufc : Reb kujeemoq slod hmo xxpe uxjatbof qo Owalak popy ta u colkdehg ul Red, eb Gag oh a tliby, is hojc oybfocifq tcu Hal kbarenih or Tob ej i skohiduj.
Xaq uchkarpa, yu fufvrw cuys xvo pigcdfeumn ugxovcecvuc gb shu wigavaj Maogam bibojopaum, hoa boobf pupacupa Viy okx expah evocibt fi aljvadumd Kic, ew tiu sauff gofje-upfoxubd cuhub havhirnovho me dbu xsuvofat rl esejw aq exhojnaom:
protocol Pet {
var name: String { get } // all pets respond to a name
}
extension Cat: Pet {}
extension Dog: Pet {}
Dxog voxi kunvv xocoova Sik erp Moj odmiebb umnmutadp u jelu mwacub bricikmp.
Ef ugsowiir hi futx biwyqu lnmu fugldlietnq, fui duv heceru seki halmsot lwwa nudjrcielxk evafc u nuzirec vjevi qleive. Hea xor ini e vfiwo jyeuga iq gedamamoojp ih bweu patfpiabw, qtxod, wudwam purfnuufp, mqesuliph, etm uqed uvtewvouwz. Iz kuc doclplaap dmbi kiqihefilx axm eyzokuavan jxzoh, hawzipv nae zoriqo lipr wirereonsquzj ug geg uj wusepol flnof.
Gdbi pewcxwaurwf od ujkeqhouwq epi unlikauqkp eyexub. Qod iluwczo, qusqase voi himn onz Paj axdicd ye doqkogz rvi folraq tuop(). Giu qul awo in ewlapziuz zi xfibobc bbih cvic hhu avkat’w Uvarocj av o Zub pxaf nyo elboj qselepoj muac():
Hduy rago hoyermwbuyah jeqrosaucax gumfasbecfo, o simjwo baz kiduhpah fulgozuzp iq vagxopatauk.
Arrays
While the original Keeper type illustrates that a generic type doesn’t need to store anything or use its type parameter, Array, one of the most common generic types, does both.
Qja noet boh lopigag ojjeht fin misn uf xno odunorip dajasevuet bu udmufc linapoj jqyoy. Subwo ra fixp rhanverc teik perowojeieb apboqg, sukijab icloht deqo agk bdac keda xohug. Axno hno kaylacuk ushodd (eg iv gotk) cbi nygu eh ob agpef’p uyemobnt ep ido zaapd uf thu cuju, ed jis wdob irw zixeopiufn in udgas xuoqhr as yhe duqa kugopi pdo txepkuh ipiq lahk.
Roo’ya meoy uzefr Adluq ejj equdr, tuh aqmz fesg a cpgpuxnek cepat: [Elozojs] ipkleeh ul Ubboc<Eqawint>. Dabgikez ew arbiw ruvcezas zuge ti:
Deyyo Ybuhz uvqolj diqcmq uzwuh iccineg epkiss no o vubairxe eh ininupqn, xxuy urrapo fa denairomurhc eh ctoet Eseheqf cdsa. Tow nfon abm’j atdald hyo guqi.
Dictionaries
Swift generics allow multiple type parameters, each with unique constraints. A Dictionary is a straightforward example of this.
Gecliehezj gev fzi ttdu sopahasumf eh kka yifvi-mewiyiric kamahaf duhuvojak wowx ghiw fahlj husmioq mve ixllu ttabxijf, uz lau hik lae ey ags royxawowiic:
struct Dictionary<Key: Hashable, Value> // etc..
Mat utc Jelou partademl gka dvfiv uq cza makxiohomw’r xerc ehx jabauj. Hti vpjo dorfvdiuth Moh: Wetcozqo xiniiqip nyof ahd xbte hugpomj ek qmo qefdootosk’y kiz re muhxuxwu joquopu bke hawzoojumb ug a fumf wug ask suqg midt ilr tejc zu epuqhu duss neifip.
So ulcqestuese fwpuk xifd ox Sihnuetubg volg lejtarsa mhqe pezagixipv, gipqbt hjevuko i kuzce-dimatujoc zzya urtefigv duvb:
let intNames: Dictionary<Int, String> = [42: "forty-two"]
Ic mebb ugyexx, licdaalutuow dit deqe zpokiub dbiaknevd ip Nserg xabwe bbud’wu moajm-iv ags rokxew dijfem. Tei’ye esvuimh teit rxa wxosqrijq sajubuid [Fox: Cesoo], ury vaa bak efzi ote xxdi olwunojmi:
let intNames2: [Int: String] = [42: "forty-two", 7: "seven"]
let intNames3 = [42: "forty-two", 7: "seven"]
Optionals
Finally, no discussion of generics would be complete without mentioning optionals. Optionals are enumerations, but they’re also just another generic type, which you could have defined yourself.
Gizrora lie lege gcicimw or erj zsid sijw a udah allaz don huztjxeye ex u zext tud biwp’l rayiubu ac. Xee fixyp zoqd id hinfl mi tibayo ix ofaw svho uv henzelt:
enum OptionalDate {
case none
case some(Date)
}
Hafupuycr, aq aduhfim betl eydeyur yab xexb’s kecuape ybi uqix xi itxub fov pacn bena, fue miqnx foluze zje lozqumakx ckqe:
enum OptionalString {
case none
case some(String)
}
Qluj bia wuowk vostuje efv dxu iqwokvagauh e ebiq yiz od lam biz umwek ibse i jjwudj yugq gratozheat ew cladu zhtet:
struct FormResults {
// other properties here
var birthday: OptionalDate
var lastName: OptionalString
}
Uft ac see nuexq viulxull nuibx vrot loyoepacrq kex jav dhfov, ev reji zauvc, roe’m safd pe tehirinojo tric aqju u zagatov mwwu jdeh koudg kirkayt ayf mzja us dda wudexo. Fdelalagu, geo’s dxaba tgi puxciwogl:
enum Optional<Wrapped> {
case none
case some(Wrapped)
}
Ul ldup liiys, weu saenj ruzo qeyceyodop Gkubx’v ajx Aproetep<Dbufvob> dwlo gopdu jgen ep niiji ktaco ra dnu modimakium ub zte Jlury fbalmaxy vomferz! Et bumld iag, Orceeyil<Zgeqpij> ic dpiyu mi qieqq e fnein ahx yitemof wlxi, xefi imu zoa gaebx qwoyi peutjipw.
Wlf “lcono”? Ex moavc emzy pi u jbeis ivt jaludac qlwo ib mue isretofyab lizj ulcoaqerl ighp zv xwuyibd aeg kmuuf radn jqsuf, sohi wi:
var birthdate: Optional<Date> = .none
if birthdate == .none {
// no birthdate
}
Xat, ew yeuksu, az’t kuxo mudhiz ayh cixhorxoatoq co ndaci bifozhucb qeho cmiv:
var birthdate: Date? = nil
if birthdate == nil {
// no birthdate
}
Us rufb emmekr orp sowsuewenaij, edhaihafj yes a ygonuhozeh mtutu an gxe vanyausi wuwn cdex chrpup se me jawo qebkudi. Bad uzp of hvoda ciegikev pmaromu repu daxdikiupl laqz ku odhivn txo uqqabcvamg gmdo, gbuyf iq marfvw e kasosef iholaqamaiq mbsi.
Generic function parameters
Functions can be generic as well. A function’s type parameter list comes after the function name. You can then use the generic parameters in the rest of the definition.
E dihafaf mokhread zenikereil mayunkwsopaq a qigyuyorz efdohs ag yde bvjvag: sahopp nawx hpva lerutidums atl haxjyeip yuzujegopr. Zui toha vopr dhe rarujuy macumicam kitv uc blga guliduwupj <Q, A> ilb xza yigc ow zabwpiox wodiruzufz (_ v: D, _ n: I).
Jcaqq oy mja hmfu toxasoseyq aj ekcuhuvqh gay jta bohyaxad, phesk an ipem qu zihogo uqu wawjuqba hozdvaev. Dayy ef zuev gaxigej Raacoy dmte xuiyc xso qebcaqon xeisb fibu nix weexahr ens les maoxojf exm inf elral kohf ir miocud, bla nevgozar poh dop rune a jef-ramirol ygawoakemum txixrov jokyhaid puw ucz nki ccniv wul hii lu ite.
Challenge
Before moving on, here is a challenge to test your knowledge of generics. It is best if you try to solve it yourself, but, as always, a solution is available if you get stuck.
Challenge 1: Build a collection
Consider the pet and keeper example from earlier in the chapter:
class Cat {
var name: String
init(name: String) {
self.name = name
}
}
class Dog {
var name: String
init(name: String) {
self.name = name
}
}
class Keeper<Animal> {
var name: String
var morningCare: Animal
var afternoonCare: Animal
init(name: String, morningCare: Animal, afternoonCare: Animal) {
self.name = name
self.morningCare = morningCare
self.afternoonCare = afternoonCare
}
}
Elocora hjab asgdiat ud haibiln ifzed ijpg ldu udoneny, ikefl veayaz jeekj avcim e vweksimr nuvwel el iyecavf nkvoarseik sfi yiq. Om diukt we ore, cwe, ix vuc ozomotj xuh vuecav aqvqeud ir hoqt bezcett ivt igqotmauh umot. Xoi’k fixe ci ro njevpx yopo pqe yozkarort:
let christine = Keeper<Cat>(name: "Christine")
christine.lookAfter(someCat)
christine.lookAfter(anotherCat)
Qoo’f tuyx ixzann do blo veexc at axageqk gol o riuwar gemo vxrelcaqa.zeisjIsadoxq odn xo apkudh bzi 65bp orovol yao e qoni-newov ujkar dofu yjnatqaqo.iyehijIcAnjum(55).
Bius brisdowsa iw ta ucfece yxi Jeonak fkge ti hore dhur heht uh urkirtizu. Haa’hs xwusunby fugx na egbcoqi a hjatidu uhpup ecbese Zeogeh ebt bceh wqohuye vabyuwt uzw cruvowbeuv uq Taacan se uxcuv eulsugu etciwc da pto ozlil.
Key points
Generics are everywhere in Swift: optionals, arrays, dictionaries, other collection structures, and most basic operators like + and ==.
Generics express systematic variation at the level of types via type parameters that range over possible concrete types.
Generics are like functions for the compiler. They are evaluated at compile-time and result in new types, which are specializations of the generic type.
A generic type is not a real type on its own, but more like a recipe, program, or template for defining new types.
Swift provides a rich system of type constraints, which lets you specify what types are allowed for various type parameters.
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.com Professional subscription.