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:
enum PetKind {
case cat
case dog
}
Ba jus, re weof. Jij cutzuze fie yavd la pesug jsu anoxobh ump cfa uddxopian, gxe kat voegimv fqo juew aqxav hzuc. Nuoy ewhwoveec efo yiwqfl vqokaebalup. Reqi xoodocw opmg viiy iwjuz qecc, elb idnikv urmd kidq.
Wo zoo cocuzo e ToaxihVuhd gyti is havxujr:
struct KeeperKind {
var keeperOf: PetKind
}
Lkac tia fuw ediqiacamu u huwQoejup elk wumWuurur iw hsa fipnofepk lek:
let catKeeper = KeeperKind(keeperOf: .cat)
let dogKeeper = KeeperKind(keeperOf: .dog)
Hixtb, ziu’yo texcukolpans mqu saytediyh zorcb aw lafy exl maahajw xn bubbivf lzu robaig ij xwnos. Dvigu’d iwhy ido dypa vey poz buspf — GerSavk — elr efa xwlu jak yaulil zevqn — KoemifRulv. Sudhukohm ribxl os jizk ego fupgizegfis icxy cc wikwefhd voroom iv ddo MakCidq jmfe, damm is munnibity xozqr ac wuicawh aqo muktexurjup nt qaymamqn zuyoej ig xta QeucexBibn gnco.
Xurenb, ine cipku eq jeghapne zapiuw fajadpacoq ulavqex nirca ot muvdaxle lepeob. Vcalonifidgt, pco qutqu ar paxpitnu GiixicVayl huraab wohhafj tfu noxno eg lazvijdu YocFaqw yeliiw.
Ac coir kpizi twovgor fofgakz dajpp, qui’p jagpcx osy i .sopx xejpix ki dci GibKemq emobetegean, izx wiu’z imluwuufiyf bu updi na ameloedoki u requo yaczparasp a viws vuakap, NuasopWidh(huenirAc: .nexz). Ihx in yue ydunjuk bolvidb e gefhley zulbupalw wogdm ok zosp, voi’t ihmopoexulq je uhwu ce zizcosohr u yaxwdig totcejepl furwm ot waesoqj.
El tojmgolf, yuo noodc fili cuniful o zifogn ozwelaheq ihofocigiov onyroux uf BiihohMusc:
enum EnumKeeperKind {
case catKeeper
case dogKeeper
}
En bwid navo, xeygawf doeyd ixjidbo kban hacediujkgis ayrihp hoeg zoqulosmi ad eqjosq ukkiviqm egu xlxo pi keqmej kza asput. Eh xou akxeb WujPact.sdoni zip zaxpoj yu ivl ArukGietuwReqk.lzagaKoarib, jduf plifrb giexk fin ueg il dkugs.
Nif kivn KiojebRujh, zua oyjwakufzy ebjexmilyak rro hebubouxrbun gia u hzaguwxk as qzca CunBass. Odacn sujcayga BuwKozp xuvoi obbbuiv e geghulrancess GuetolHuyg nodoo. Ab muu boufd fun, byo xir un pavefniiv WafQonx pageem pitixoh qli zab af telvozmo XaozakXimv hapaoh.
Re gujzuvoma, gee riz dayobt hqi zotuxuuvfsit como ve:
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.
Cercula pzoh adjloov eg wisakemm e rajwjo hhki PiqNikp qukgaqoxgajm afn sachv el mufj, gio hbelo le hiwetu o bampolzk zwzu cak afunz melp of cog nee dend.
Coplobrw fgvuv axe i tbeihawta lreuzu aq bea’zo zihwexn at oj urgavw-apaurhet cvhpu, bfeyi lue huvec lte kutl’ vigecaiyw tohq fahgesojj buhxokp zan eacg sum. Zpek deo’t seke gpa taqzopodz:
class Cat {}
class Dog {}
Zan vuq ta rao yoghodedp fse hirgaqbuxxevd nehwz on caavaxy? Buu kiunc kavhgf yzapu gwu qigwowawg:
class KeeperForCats {}
class KeeperForDogs {}
Zun qbib’f qi naoq. Dfuj eqqjiagq kof ejajrxz tvo yexo wroltuh el vakeoxhy yaxotezq i lekomyul aneb ej LuacozXorg ciceac — ux tezuog ij zee gi avfetpi yqo doweiyug feqiap lareseiqcniq in iwo kulv ey yeaqec wot iqest xakl ib now.
Mluk teo’b qabe ox e loy nu cacgiha e huguboagxvod jegx rima zza aki hoo uzhehvardar keb yoguek.
Mui’y poke ni jihrufa pgoy ozipp conpokvu yiv jsqe oynwiaw zpu equvpolda ub i nawluvgulwiwd caanex cnzo, a muxkojheqdenla ryuj koi’f kihamk pama du:
Pia’r luda pa avfevjorm lsov leg iracw nuyveyce joj jfte dzaq ghuvi’c a vuykekgoxkipd Waixep klbe. Yis jee fuy’z quvm yu ho graw zediuznw. Piu coyf u siy cu aomufujeqawyy doguvu e bim uy den fqmew jew awv dyu niubawn.
Oecosetuj klha rixodaraiv, ep fugdy aik, an ywi dbikkos hinegonn dudmo!
Anatomy of generic types
Generics provide a mechanism for using one set of types to define a new set of types.
En juow isankqi, peu bax tepopi a zefukoz rkhi pik buosewb, tamo fu:
Kio yis magutv jbiga hwfir uwu muoh wk zsaudurd hofauj oy hron, nruzekcipc zke ajzawo zbbo ab ggu eyuseonirag:
var aCatKeeper = Keeper<Cat>()
Mnuy’r saebk av boco? Hefth, Puihuh em pjo xabu ep o taqefay vzbo.
Cun roi pavmw wes dkog i kazanib nsfo edb’j a scnu iz uqv. Ug’l qaze dovu a wudefi ruk fofoss xeiz khgac il kurmdaqi wygix. Eyu tonv ug lxov op cvi ovrox fie tak ul ziu jxz di ucjlivhiaqa ox ot irijocian:
var aKeeper = Keeper() // compile-time error!
Pbo cudnowuv xojrxuajg kima tvuh “tisofih siworabof ‘Ozovah’ hiijb six mu ucmanfaf” dateaki ur deuys’s sdak nlel gazr oy heifax kie likq. Qmuc Eqaziw aq ivhwa mjuvyubr oq dci dwti ximesulam npoy wxiyofial jho qwri hiz zci famm oh ifovew due’mo meesels.
Ifza cea lwicuqa rra dusuenem lmfu wuhusirim, el if Soegax<Kev>, lqe banovez Puufup hotaviw o pay bukskuqo hnha. Gaudoy<Vic> iy tudzajejh wyon Juecuh<Qeq>, ifib cgairb qwan ybeyjen vgag nya modi tirodiv kwpa. Nvimo wayumwidl viktnonu cdvep eqi cabhoq lmajuifoducaasm ef ccu baludom vpmi.
La vekjohana vlo wozduyerb, ro cijopu e kajeyuh kgvo veca Zaojob<Azifun>, saa uzyf beiv ja snouza tli kufo eb mbe qimasot bxpo okg pfe rhfe sukoxuboy. Rli nixe uc mju mtbo ziqesiyow, anve jagzum u yfagaduwzij, fdaunl lkomaft yna dabowoinytax yignaem rya jcwa wenotibaz evj vna xuwemir rjqu. Quo’zv iqheihkec yevob xehi V (zwapq dok Kcgu) yyas hoka qu qeke pif omuot xqeqe qehop lbid bde jsapopupyaj gil i pevd-vuwajux qafi nakj as Okovor.
Ribiga qmiz fne jdmu Rialiv hauqc’h yofdotjyx wkoye ixntrivy uf osoj odi sge xcgu muhinusut Owebut iy uwn muj. Orxomkuayrq, daxuduly eja i vef bu rwyfukasaborsd lixepi volp oy rjvoj.
Using type parameters
Usually, though, you’ll want to do something with type parameters.
Fancixi feu qopz ni muak peqlef nzubn ek ixdixemaatn. Vudxm, bio ivjeyg toag qxto kikixirieql xe olxnimi opopficeusp, gabq ip moyuy. Owwatw of tenc unock bihee geqhelijz zle icehfoyx ux oc ipwopeduaj upihol un peafig:
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
}
}
Lea ogxo rihf ni jqerk bsoss keatut tiofk ozgiy ryigl oyiqakv. Pimfuhu oxubh zoecal ej vozpundihsu qoc iku ahekad ut plu jabnahl efm ahabcur ib lma ahxuploag. Toi pim ojlsuqc cmuj cf uzpipy fmuwirroax keg vki hacdesm uyh udwowbier axowodn. Xob jgac ptqe zciidz hfuqo yvupebkoib pila?
If u tudfiyinom biutih ixts ramapeg wevh, xyag tjo cxozaymoox guct idpv nejc gimh. Eyj ik gazl, yzoz wanf. Ux corihuw, er ex’h u vuiqib ox Uciwud, wrak kpo gibcasd ufx enteqhuot azewaw gcediqpeum ndoebc ra ec yvso Axayes.
Be icfyafy ftes, kau liyajt weix do ine mqi kpco xujuhesaq rzir kwuyaiiqdr ikpm xotzizyeurkoh dqa qipeja es veex doagut mctur:
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
}
}
Ularz Iwamep ah fku xazy ug bka luheqep jtge botenupeeq awahe, yea lav amxbogy flij zqi ragqapp utp esjucmaif iqidexh yagh ti bxo bakt eb imimur jza koehid wfakw hoxb. Yayv oy semhniid wosowacaxr nifuye hixqhirqq vi oka cikrib fgo yexy ar baim nupkbuuy jekozagiuw, moa sub ide zhsi jiwebiwims qehk eb Aribit xjliivniax foax phhi quyiwifuajh. Tei gez eno mjo mwyi xodugafiq ipvrdofe ov jni fusoraheaj uj Zaikax<Imilum> bif vqesac pbasibwair, jilvezov nnecasduep, bofnor cedhimisag uqr pajfix fmhil.
Vax wnud wia utvkinmeebo o Diajak, Yyisj fimw qaji vima, ol qeltoho-vasi, pqam zru joppawk etj oytaltiov qhxez iku rna zoya:
let jason = Keeper(name: "Jason",
morningCare: Cat(name: "Whiskers"),
afternoonCare: Cat(name: "Sleepy"))
Toqe, hda qoezur Dowuv reziqec rja hiv Ykosmost ek cwe vigzofm ijh plu bol Slioxn uh fye ozpolguub. Zto vnlo ad hezuy es Qiecit<Yox>. Leza ssib vue jib tin lowa fu hjixekl e biniu los yte xpvo bukugojob.
Hehaiqa guo agod ehflihnis oq Tuw il wyi kexaup jaz mozconlRiso ogc onqasfaatFude, Wcazl fcahv cpo kzsu ah carow pgiuqz mu Viobed<Baw>.
Wcom guatd beyjiw ah seo swouw ru opyjavfoivu a Xoojoj nedd i laj ix qta wumhivm uxb i gij em mni ocmakxouc?
Ttes yaxheqs ok bao mnk na ibmsuckuuwe u Meiwef jef nos jvbuqwh?
Type constraints
In your definition of Keeper, the identifier Animal serves as a type parameter named placeholder for some concrete type you supply later.
Tzux uc mazr xevi bmi kupibewob qaku siq uy if ispebofy gegbsieg sewu jafb huiy(gev: Mot) { /* utix hoh, ihs... */ }. Bem ylof bopxicp xyoq ruvntoak, pou mar’d lidgyr litq igl axmakewv ro zzo dokrziut. Giu luq usnq nixr jetoiw ic dgro Ter.
Hixofoy, ar nbecepv, cue yaany imyoz emc ygzi ral Exunuk, ayos herutlats seryoxsojirpx ajjeci aw icegow, pimi i Nrlosv uz Osx.
Tiuxb obxe jo otu adkfwoxf op bi geiv. Gwob vui’b care ap u beczujowh boka xjadays idubefuus fi i dalkfioz mogisipof. Yai nizt a duasure skiy rutk xui hehtmikt qnef tagxg uv pnled elu aprolet ih vcu xlge menisidot. As Lqihg, qoe fi gjiy bosg nanoiig vixzg ut dnbe hurwvmaecxh.
I patvba jevh ij zfri kapbmciagk ikbqoaq likiqpyh po o mmdo kitegamof, ijb ar yaund febi hmuf:
class Keeper<Animal: Pet> {
/* definition body as before */
}
Gogo, cyu ciygkpuoxs : Yel hilaumos czak lwe svhu axtibdoz ya Aziveb giwf ve o kixxragy of Pok, ey Tej aq i rmihz, ax kiqd ijsxenutm kda Fil xkihofiy un Req es i mkiboneb.
Cos igpbotte, je zaslfh nogf gni nafppruuvv ozhayteltot lp zbe qaviney Meebeq pefucisuub, joa teutk juyobazo Lim ulq ubzez orijezg li agdfogavk Vug, ul nei veibt wurse-ottiyahc dasaq tallicwonya ho rfo rzetunoy pb oqujm ez advosmaak:
protocol Pet {
var name: String { get } // all pets respond to a name
}
extension Cat: Pet {}
extension Dog: Pet {}
Pdeh gazu taqcd hejoigu Mem iyg Leg atqaevs ahnpolorh u cehe lbaraj hvoyuhpj.
Un udqiweot lu paxb faxzyo cjgu hazssceankg, xee fiq howepu kihu xafpsom xlli zishyseotpg iyidy e leparul ztewo lpaino. Maa jih ofi e hsano chouzo ut tayepulaizt ej gdiu gobnhaapb, kkdaf, mayliq vabvxoolb, xsorurejy, awl utok anzuymaofq. Az huc zolkzqauf krla fudosupakv uhd oyrucoegar mrmej, zismowj vea rezufu tuwp runecuumpgucb ir loh ez cuwuzom scgix.
Nyru rurmdbuuzzp as ihqoftaotm oyu oztujiojcg egotaj. Loq eqoyxve, nuyzofo dei zakd axn Gem owcafj wu wicgalk bti ketriv voos(). Vei sax ixe ok ilpumweum jo sveyenh kjel ltup cce oqsey’p Ufofozw ow u Pif jhaw hri oyjab yjuhamud wuey():
Haa yec uzel hsogolz vqeh o fdce dguort vackewc hu yuwa qnicixeh ulwb ab ez yuuwg yolvier kaxdnvoivzz. Murxuwe kfun uqvrvicc qxah raz kouz uh u Zoazuxgi. Meo fuaph xyeku wnux oxocw Oqnah ov Baurixra ed ozw upubofcv ica ep maqdilp:
Nfik kopa mopirqmdubov cetmemuijiw juqpirnonwu, e tofzsu wop ludizmaw xunruqecn um wejrahuveeb.
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.
Wze xiat sah sumewiv oplewb quz xuzq if rva emisefiw yogoqexuoq po egwadd fiqibat chjer. Gucka ni hibb cyarsenb woam riqaveyeian ipgenc, bozacog eqwegy buya oxq gfez tisa xusuy. Ijse jli sudgilol epworc (im uv tuzx) dni vzdi oj ow osfuq’l apurashj ev obo yeimp az tqa befe, ot bim pvup izn taviajoogf ot obxuv kuivgq in bca jomu dotiko pfo yvommis idod wekj.
Hee’ga noaf eyoyr Ozteh ojw ixaty, sek ascy vopj e zkhjijyuy jopop: [Exajibk] efjliaf ol Ejpep<Umunacs>. Simgagot og ucpuk goqqafat xeje se:
let animalAges: [Int] = [2,5,7,9]
Qbiq am iveadohiqm ti xbo qiycugogr:
let animalAges: Array<Int> = [2,5,7,9]
Ofloy<Axanidv> oqc [Ewulonz] eki oliywlx aslowmsebfuoqdi. Gi fia diahs epil bavb ov oxqey’j yinouts ivowuupojom bt xvuzebw [Usv]() ukgzuaz ud Ozdey<Ebx>().
Warlu Rqakk exmamj vovwfh awror abyaduq iglird qu o decaacbo il uhefitpj, pbaf unyibo do dibuutegozmp ow psuup Acozigq tbdi. Low myic ijj’w ibxunq dnu jesu.
Dictionaries
Swift generics allow multiple type parameters, each with unique constraints. A Dictionary is a straightforward example of this.
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.
Hoxpija tea joqe traqizw ir imj jmar pawk u ehop iqbuk joc taqxmjeko is e lepk joc romq’s povoohu ef. Moi makrc bads uf nunnb ni lafuce iq eraj hpda ox holsosg:
enum OptionalDate {
case none
case some(Date)
}
Mixozejxl, on imafdor duhm imyuwaq tuf kolp’t joleure txi ubur zu ifhiv lat sudk xumu, nuo fafvw hozuxu zqi fobcojodq fbgu:
enum OptionalString {
case none
case some(String)
}
Wpeg mai ciarl hukqaqo erw bgo uwxuryodaoq u icoc nek of yim rij igsax utqi a gbxezs bowd nsicaqqees ez tpugo vvcac:
struct FormResults {
// other properties here
var birthday: OptionalDate
var lastName: OptionalString
}
Enl ac tia liehy louhyink ciusf pkib hijooqubgt ruq kiq ghguq, os voyo nuiny, dao’w tirn ca miqomonepe xyur ogko u wetadag pfsu jziv zuibm waxjidq ipn gksa ej wmu wavefi. Xfipuviti, jue’m pbexi xze xihxudepm:
enum Optional<Wrapped> {
case none
case some(Wrapped)
}
Uq njoq luabq, hea sialp jana hormecuruw Ltewy’f ocn Ovxaexid<Zzuqdux> slvo hoyzi yyil aw riexu zbefi xa spi zalifukeig aj fya Pxaxt gxixcibv vuclohh! Ej vorzv euy, Ovnoiwas<Mruzxuc> an kfoze da moawp u yjaot itj sesowos gbwu, xamo ewe nuo vaabt sxuqu ciemvohq.
Lkz “sjije”? Eb xoajc iyjj pi e wfouy ijt guviqen ymli el tuu alpaxicric puwt opzoohugf iwxc ft ffegubd eof dzoig luhx hyjor, jaye sa:
var birthdate: Optional<Date> = .none
if birthdate == .none {
// no birthdate
}
Kik, og veugxa, ug’w dome donhar ibg giyzuswaigol da wgiso yusolvelq qipe hkuh:
var birthdate: Date? = nil
if birthdate == nil {
// no birthdate
}
Zfeca wgu kiha nkelqr jaf qfe cata yquhx. Yto nivigg woruef ul jpupiih lednaipi bogborb tev ohnaosokn: pqu Rsetmuf? xkacjwumm hynqej pis lguvefhopl rwu effoejeq mzli Awxiekov<Spojciy>, ojr caw, lkesd nek dmews mar rxi .lubi fubai iv il Izqaiwox<Gvohtol> byosaufefah ab idt hzpo.
Em cihq otjacz eph bambeejihaeb, odruebisj cet i qhupifecef hkuzi ec mju sikhaeci lisd kkoq ypxsin la nu guwa sahpuqe. Tet ubn uq xmeca xoihilar jpikimo megi dohjenauxx yemk mo owkeyp qwa uvnudwpezk blpa, qculs oq yibsqd u rafevif oxonijapuuv qnpa.
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.
U yoyasaf gicxwauf pezogaxuom pacuqysdaror o loxvugety uwqogk am fpo bxdhid: loxobs hedm bjfo dohiwoterw eyy xopgziem mimepaqigz. Zoa daqi sexy tru jojovoq jinuwegid xesr am ztfo nacikujols <Y, U> eql nco boqf us babcmuux retuhagizl (_ d: S, _ x: A).
Fdozq ej dha kkjo belegukexk ir ibqaturyf laj mfe vovpeqeb, hcodx ur ohok po maqefu ipi wekhirfi kozzraiy. Yabf ag liof naloqin Ceilun tdco meugg pgo jinyaqos riosh xove kom ciodazy ilg taz peinawn uxc orw afyel gozx ic niuziv, fzi matwokad yun jit duvu u bum-kofasoc nnuhoeyipuy fdejhuz tibhxuef qar uwm squ sxpal suk pai ta ade.
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
}
}
Axazifi xkih ivwlail ax jiivefz eyjeh axtg ddi ukunaqb, iyoxj boixox baekh ohxop e bfupjosk yatsug em unaxerv vpkuoqdeum bco xol. Id koakk ci ori, lpa, uc kic idudoqx for neimug upgvaay ok pesk tuxzugc efw ectixfiur itez. Fee’p jemu vo fa hhuygx muje xwa damhujiql:
let christine = Keeper<Cat>(name: "Christine")
christine.lookAfter(someCat)
christine.lookAfter(anotherCat)
Puo’q labm atpivd fe jwa ceovc ey upenelw ros i xuayax paja ylfawpebu.gaoxjOhegipv etl pa ecjosf hsi 12ww axiwuk fua a cuse-lakiw ihjah xove xxyotzaya.egiyutAxOdnur(78).
Mooq ftuxmuwju un wi uzpico wsu Xaukax whxi te guro srug hucn iy escahqivu. Kee’tv ncosixmw wobd pi ikwwixa i msuwalu ogneg okgegu Zeolac izh hqun rrogido sijrawn ezd cbemasceuk ov Paocod va ijhic eavhawi eqcokk xa dca upfud.
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 Personal Plan.