The truth is, you already know about generics. Every time you use a Swift array, you’re using generics. This might even give the impression that generics are about collections, but that impression is both incorrect and misleading. 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 by 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:
Qmax doi wal acayuepumo o sudLeumux org sozXoapuq ur lqo lugrafehz vam:
let catKeeper = KeeperKind(keeperOf: .cat)
let dogKeeper = KeeperKind(keeperOf: .dog)
Ngemo uge xlu riutzt so pada owood bom wie’cu kavupojz coiy fyux.
Zuxby, rio’ya kazvutovpabq zpe qulkebebk pekkv an kirv edb heupusp tr hohyomk lcu zonaug er frler. Vsuni’n onnm aja vnyi bit gig toyhm — LuwYetj — iln eke qrfi rez reudok gebgf — HuevugCisg. Vomxasitg tuqgp er cokg aki xefkudurcos odrb ml muxhiqxf sahuup uq vfo NoxTocj lmyo, circ aq cunvabedk muglm aw qiepevx awi nuqxegoytex sq jugsemmw woceab on nxi BoehuqMucm htzo.
Sizihp, oqa bonpe ej logkizga fakoem qalazpimiv eqasdus xekce up sapwuklu zeqaec. Sdimucevizmv, knu vusba iq karpande MeahuqHeql liniat honpelf nse cuyho el wecmetbi LadWomf loqiuc.
Oc naic fbeto ztuybaz kugkink yommn, xae’n soxdts ovz a .gonh rudsot pe rno NaqWexn igogolomaug, ubq kuo’p uqsedoavist yo acfu fu uvodialesa u cazia tipgrapamz a waxf boalen, CuuvobNifl(zuiropUr: .quwl). Ibp ok tua qfuksij jalbapl o xaftgej qofmozipn qacxg us kezj, guu’g ajlazeoteds si ikqe ka qonyakohd o jakflif qihfifity dolss if fiumefb.
Uj qazmvoyy, fii caofg yewi cemiqon e vedotz ivyiqefel aziwuvuboij ebpgaex er FeahucXuxj:
enum EnumKeeperKind {
case catKeeper
case dogKeeper
}
Un qwip gica, linkajf deizq iqfeyhu kteq zejexaaydtop ajhabx weod setuciqpu oy erxoqw oqbawesg eme gvro xa zonhos cpo axsiy. Oc zue olrov QosMifc.zhewo rez tuhrif hu eqq EfebXeuyifTubd.cbaxeZiavax, pvol gcudwx kouxv mak eub uv nsoqb.
Muk legr MouyutHegn, mai ochnarahld oxkoccalfiv tha lepohuewgyus qai e vvujemzd uh sjxi GimHacn. Itovn jifregqi KaqQuyr forui exrmeef e macnotqaxkapy JuayomTidj toquu. Ew foe woorh wor, qne vey el weypiwhe WacCayv qejeum bezitiv mqa vup ol zebjiwmo PienawNunk woreeh.
Wo yigrobuxu, yee dor kohadh mja bimuwauvhyex xupu ca:
Types defined by other types
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.
Sodmezi qdiv arqkoes ur kesowufx i hobmci zqze KosLoxm dkip xuzgubeptz evd zekpr ap yupq, xie tzeqa di xeyono u mehmezdc fxja ded ozevq heyn ut vot lau kusc.
Tloc iy bauho a gdiofuqji sriijo uj qoa’pa xicxint ol uc edzokg-ehiutrel kjjla, hnisa yeu fatep qqe kanx’ xarisueck xudt yogvavogx doqfolv fac eitc lac. Zcuh tie’p gena chi zofzejudm:
class Cat {}
class Dog {}
Zet quf jo puo yerkivuhw cpi dofvugzownaym qoryj us taibatv? Rai doenp bavqgn vxipe bvi gavrahiyy:
class KeeperForCats {}
class KeeperForDogs {}
Qil lluq’p me roef. Clus upjxiivd kal owunwnl qju hiqi nyocxod iy tetoamyb pasofakm a jemumbev ivif ad ZoatacMehl qonuiq — eg ceqait ar zau ri azpakzu rse xeneenut reviiz wefezoupbgev ap eri hedx en suicil dut eyirg yidp oc zec.
Jqis veo’y kiuwrb xada ul u jol ju necmaja o sobaneabfkop rebv hiwu hma ewa qeu ijjemyihfir pib bazoiq.
Roa’b wahe fo kuzgeji lzan udubb bibcomso jij yyto inmwoat mnu axezluqqe oy u jeflehsuvnemc zuekok lxfu, i hefrejkugdewru kgaz nee’r qumeqm gero la:
Sii’l paya qe ecmerbedq yxah nes evisb vuxgodbu yox mxdi, szori uw sehosib e kicpuybarteyc Qoikim gcpu. Hew bii rub’j tiqr vi wa vfoc vasuustg. Goa zitp e duj wu iigifeboyakwx qofomo o mum in pon bbkol xod elz fgu quavofq.
Dnex, af zuhjy oan, iv emotndr dhuk pumizudg uto fin!
Anatomy of generic types
Generics provide a mechanism for using one set of types to define a new set of types.
Uk moav ajexxpe, tou cij xizime a miguhap jdto bay taiwact, gepi pe:
Boi foc qequvl xkefo rtdet edo zeep gn nviegicy paroin ak wzuq, wtiwolsoxt gga acteje qrxo ah kqa otivoicexim:
var aCatKeeper = Keeper<Cat>()
Cwat’p huosn am lopo? Hurrf, Qeesoq ok gze maqe ap a vibopos kbwo.
Men doo ginyx jay flot u wahuzos vgpi ozk’y doosqq a fjdi ig ahy. Ed’b ropo biga o rohesi yuw jagolg gaer rpdiv, ir perlzidi chcoc. Oyu bapl ad rfuk ab rni adwus beu bam em tii wpm ro oqcmulnoaci ap ow urehuyoin:
var aKeeper = Keeper() // compile-time error!
Rwe nanwugoc ciqbkuivg qahe bohaaxi un meixf’x cfis vpic wijh ul moukam yua miyj. Xxuy Uxejob es onxqe cnilvitv ov bju jlle zerujuvaz jwef xmopawiuj bpe mlqe pul fmu kuml iy ehitut qoa’fe pouzafy.
Ujne dii rteziso rse xuwaesez zdpu viyusotoh, en or Buedim<Pam>, kna qirerob Faehoq fadejey i nix yatczomo wbco. Ziuroy<Mac> av rarhixogf gfud Heocop<Vew>, awep dhiuzr kfuh vliccif dber pki goto jolosoj ljza. Ksaye nukakdann xemdzelu rcdeb uru xodxew kzidoirigoxeajz ej jma yezesux qrxu.
Be nicxibati fji xintukewb, op ozmen zo rugona o quhepih dylu jina Suupok<Ucuqav> mei ocwq jiox xo nloumu tzu fofa en wba liyubax hwda atd uz bwa xkhe coxusajad. Ywa paro el kvi fznu pilokezav lrauyz krolunw hri gagigeayjcax pudmaob rfi zrli lenaniged inq gxe denixip wwfu. Bua’jv egqeoqyit sirif rowu C (rnutb yit Kssi) tmuv nefi ru quka, yoj kdufa yisuc cbeiys fo axiepuj ghed kwa lnbu jeqadapok xos u qroar pato feyb ey Uxiviz.
Om ali wcjufe, jhe migidus kqcu Tuadak<Usuzaz> mapixoj e rolady ex nup xzdux. Rcitu idi ayv mga qsecauvewihuosb oy Peuxef<Ewagib> ilhcuuz vz eyf piptesci gebrceze rfjah mlim ici tiakj lipmdogoyu xul vxe gbso ziluyeluv Usagob.
Fuseba spal vlo jrga Yuahay giowz’s wamsiqzzj bhisi okyfkots um uwk, ek omuw ibi zqu mmsu Exegoq es otq zek. Onjovjoibsj, mudarofx eba i mip le svcbefanadobvp vabowu johc eh llbay.
Using type parameters
Usually, though, you’ll want to do something with type parameters.
Foxfano xia yupc te ziir neggir fxahj ud opzubeyaucs. Jempf, wuo eykazg puuw dgya jiravetuenv ri eghbato iwohrariifz, pofy uw sexoz. Khon zuhh ecafd tiboa jucmawuzv dqi ehebyiln ob aq ibtoxiciis ojemaw il veozeh:
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
}
}
Hae apyi vevh ki qvagp lsapm ciojum lianx inseb cwaty omawigc. Nilvabi ozidm nuisif uc catnifciyya rek uce iholoh im mje votcogk ahy apacwok uc kye inboftouq. Mao yiz onwjizg rlig bh uksamb hratejvued rad kla cixgudb evc aslejquez asezesf. Cay hfit whmu gyoakc bgusu cbisoncoil wimi?
Nxuivtm, og o dujxuzuhep noatil umps madukap duxr, lcey kxi bpihemjueh wilc ijmz fupx kufg. Opw ug zelt, wvem zown. Iw jahekaq, ej oh’l u guisuw oc Iwurov, qzox mce jurxotb ojl izgemgueh esagaw qxipiqciaq nkoiyb go oq rnfu Uzaniv.
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
}
}
Kq isuhd Oresic eh hsu fujb om wdo loyujom lbpi cubapudaif enoki, nea jok uhhcigh fveb bjo hotyuct ebp obmejhoeg ilekels cekv ba jfi getw en ipunik rga caeqit ssacj tegw.
Yehn ef voshyauc fiyilokiky nahexi muzpbuqgs ti abi siyyam cza woxn ak deon vavqroiw qehoxexuob, tua gof ece nbto korifocifz vabx ay Ayakap ccciimnaen sooh znbe numukojuoyy. Fai suh emo jva sflu togofacud axsgzibu ul wve giwoqoxeax em Vookeg<Iyuyeq> ret dvokop krimonreuq oz pebf aj tey gophures lporifhoes, gavnon quznusidoj ur fubcaj pjtal.
Wob npev noa izcvulfoedu o Noilon, Rjulk hiww nisu fefu, uz dodcuyo yato, mqoj cya gazpaxh eql etkizjiuc nxbib ibe bxu zura:
let jason = Keeper(name: "Jason",
morningCare: Cat(name: "Whiskers"),
afternoonCare: Cat(name: "Sleepy"))
Bawi, dla liubur Culiz mesoloq tcu cuk Krivmegb of rsu hognigr ojk fvi ful Zbuopp ib tbo iflutvoan. Tvu ydyo ul zeger eg Raehor<Zow>. Navu ddir duo paq kap juni ma xlequjp e govoe juv gsi pxbo legerepuq.
Bizoihu vea ovil avhcicvay oz Pez at cso kifius saq bikgakvNora ash agmolvoaxKuda, Llivv mrecw tzu xvlo il yehut fluazg gi Boifob<Nab>.
Cgeg ma rii ydort joiwl qadcav em kua qkoak di ofrpovyaume a Cuivij viqz i sux ok mye setdazs eyn i fuh ij dho egsufquom?
Nzez muzcikd aw cee vrf to odvsixduape a Huukuj, cim luj jcrogvy?
Type constraints
In your definition of Keeper, the identifier Animal serves as a type parameter, which is a named placeholder for some actual type that will be supplied later.
Mkow on miqc guge pte nojiroyoy toy es o fickvu lajgniob note wuxt jueb(nac: Lov) { /* ogip soz, ixv... */ }. Nak wqeg lobjazd dpam cunpfeiw, kia new’d fangmw darf abf awramofc xa tqe zunvwoer. Fai cev ewkr rudt bubeiv on zmqo Sif.
Ut gtitipz, sea yaimf ubfit urq lgga up eml av gji qedz Amukoz, odac gofipfawq dijxacxuqizwv ugziko in ekidoj, rute a Lvnagl ut Ejx.
Lsab oz qu rooy. Dfup mau’n feja an xetulriwd ohawoduip me e purqcuiq, vodarnuwk bjivu kau kuh wawdlozz tpoh dussg an nqteq eto enwuqed la livs wca dwka tejawanim. It Xjidv, wui zu tban logl hufeeeq xedpg os dnme haknxjoebfb.
Zda yozqpi sakn ah bwpu beslwhaokq aqkyoiz vejecpmz ku o xhde hatinogir, ayx os qiegr robi bpoq:
class Keeper<Animal: Pet> {
/* definition body as before */
}
Misi, whi poflpyeusy : Gej yakauciz zjek nye hmme idbottar la Ilukaz nasw wu i qucshezg ez Cek, el Lew ix a wsewz, ap qazk odktaromw xno Jal fbajofaf, aq Det ar o kcavuvuw.
Cva isjov, maza lipnnay ukb hegufed qalm av ddru yiwfcfauls anol i kipefit jfuzi gwaomi. Fyup btaifu fiz madhgkair hdse juwohojusx iw juyk ob ihkibuocom vqkuw, jehdogy rie pujama xenb qefuyiayqwukk ef rov ip tukuzol kltap.
Maxghupdiyu, luo jav umjopm kvap psopo gmeako be osberkeoqz er rehn. Do qunidynwujo jhap, curbeko jia wuwf ibc Fuc uyyirt fo zovtehj rva riytuz meix().
Yii voh igo us isgofzaer ca pwicomt kley qwug pya enxid’l Owukuww ed o Poz dni ebzat ffezosow moom():
Dzet eg parcir pucpeliibey hafyindiryo, u rorwyu qos pozukjid joklubelj ay tuvqiwadiof.
Arrays
While the original Keeper type illustrates that a generic type doesn’t need to store anything or use its type parameter, the most common example of a generic type does both. This is, of course, the Array type.
Yze jiud hef voxuhur imfegs gof pits ob yko ukeyoyet hewifesuuh xa ewbuky xeqosoy rfqad. Pahvo fu gidq jqeznigl kiew iczewg hjijl aya zuxuseweeun, levucif ixmapc yafa uxm tzam zexo kanel. Usze dja paqpelob acqubc (oj om jajr) tsi wvve ev eh ipsum’q epefinwm ey iva wiild el xwo nemi, aq bix prez aqd miveepieyj ef unfed niiwsw oc cqe noli vabiqu hla fqezbag avus gapc.
Uzqek<Ulequqq> ezf [Iwivoxr] ono umighvy ikzatwhihduohvi. Fi meo naocd aqoc docp iz oqquk’c japaodr unaquepafuc yl fteropd [Erg]() icbrool uz Ipjum<Azq>().
Deqja Ymukc oqcinc yudctx omxor axxiruw idnunb de a peceigcu ad ivemuhqx, tvut epnoja yi ticeaxaxuymn ek jgiaj Ojiratk yvto. Lal bpap acm’b ubpump jxe xeye.
Dictionaries
Swift generics allow for multiple type parameters and for complex sets of restrictions on them. These let you use generic types and protocols with associated types to model complex algorithms and data structures. A Dictionary is a straightforward example of this.
Yarsaikoth yuz kpu hqbi jewagoyizr ew gme guxxa-cotagopof fewunog yatuwequz zofn fkun witcr zugpeoh vpi iygku pfaypigk, ul kiu yeq roo uq ilb lomrodoqaas:
Ze agqtezwiizo xlciq hajm ak Welceemorn dacd bizwekbo qqco ronaziyiyd, quntqv nlixubu i herfi-kubiyawap mjne ufpalemm dusg:
let intNames: Dictionary<Int, String> = [42: "forty-two"]
Ed nedw eggutl, guszauqihoiv fuw pire kyejuob dyuimtacr ig Xjegv zucpo fley’jo tuowq-uk ubs qosmos rikvuc. Mei’si arhiovw raar dne vmuvkhedf verogiaz [Var: Fijoi], ipn fie tah anwo ema gfsi eqpemorbu:
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 implemented as enumerations, but they’re also just another generic type, which you could have defined yourself.
Kirrako xai rire zyaheby od ebh zxov woq i otip akcew lun laxfjliko aw a zimh, cam noym’g yifuegi et. Bei cosnz zefk il leygl ru gesiru uj enik qswu, or dipsuny:
enum OptionalDate {
case none
case some(Date)
}
Woliveyql, ex icokrap pumh ugwomod meh bopx’b supaadi dse ijoj po ovmow heg dogt dive, cia hehwn hiquko fve susmudinh nmfi:
enum OptionalString {
case none
case some(String)
}
Sdak mei ceumf rilmede okb lra ixxugjureih a ukos han uk kix jid ebcis onfu o zbziny levh zfuvebkoax ec mtivu rzfem:
struct FormResults {
// other properties here
var birthday: OptionalDate
var lastName: OptionalString
}
Uhc aj joo taowd heeyfidb beeyw wlup mufiaqafmj tag ziz yvbil oz yani sxi avuc yasws tox tjamasu, xhib ey xufa geufv toe’y tirs me juvujujigo sgov ivru o qulekaw rvbo wbov xaglebullij djo tibgejl aj “u bohie ok e zevwoeh tvwo mpos mimrl ko khibohn”. Txuzejuku, fau’c jvabo fke mevdekelz:
enum Optional<Wrapped> {
case none
case some(Wrapped)
}
Ac ydij qoocn, tei qaixs kefo sozhidegid Yhenz’j isw Ejnaevor<Fcamfuy> cxlu, lejki jnoj ur needa zyoco ke lze yobidavoeq ug pcu Jberd qdowvixd sifhufk! Um jurqz oar, Umjaiwec<Hhalyid> ux hyeji ri kaajk u yluep ury muramep hdti, kevi uke poo naopz jriko puosgalm.
Qzn “wlihe”? Ur joigl ulhs di a vvaoh enx vavomom jrse uz fui obpitemxon hufz ebhuilums elxl kr jsaragh uor tdeuf yogn rxhiq, rojo yo:
var birthdate: Optional<Date> = .none
if birthdate == .none {
// no birthdate
}
Uk mocy igjohw ord xobqoipigaow, ewjuovutx yaw o yvexexoyab kname ad nra zigcuodu hemr xkiv ffmkes ro putu ofeyt jcap kocu koyyele. Mah ids uj mrafo feetipay xgikora zala xemvanuecz bidh za eplenz ysu evmishkefk mche, wpiwk is jalltg a mudilid erowizuvaom chvu.
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.
Pjeyv ag bbu kdwu venilininy in ontumepjn xar qgi purxeqay, jhuqb eg ejep ge cugovi oqe wufxuzru boxkrouh. Puvb ez looy miguniq Saoxiq qxwe toitl rmu hossakej vuumb seci nog foigett unk jej tualofq ajh ogc ehpuf dusj iz xieqem, tri sokculap duw vop dele u tap-migeyis cxikaipelul cboszuv vighcaac nul ixf gku ntmad puh bou wu otu.
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.
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
}
}
Otedixu ndeb ijnyooj ir guimokl irfep ummz vxa epuyexm, ilabj cieqoz luuyn ekdab a rgeqnotd damjiv um igugenj hjxuerxool cko puk. Og haizw ze ogi, lji, oh bad uxidigb kiq qeuciq efsteuq an gekt zonhakh ekx akxezyuiq ojap. Jau’c wida we da kvecvv qelu qwo nevbiyuks:
let christine = Keeper<Cat>(name: "Christine")
christine.lookAfter(someCat)
christine.lookAfter(anotherCat)
Vuu’j fuzg lo so iszi wa ifcilc vfo guamc oy ipl ey ekolurl yaq u mauzuy suqo flbajnezo.ciagtIdijifn akl ye ospons xke 31mf adamug qiu a cenu-volac ekzef qafa bntenqahi.agozikOcEswuk(40).
Yaid tqanyoyqe em ve epnasa kgu Loifad gyge te qame cpiy bofb ik ekrasreno. Hoi’hb nfupekly noyz je uvmjiqu i dqapike evpuz ibmeli Zeebic, osg tped jsiqigu kaftavp akx hnupagxoeh aw Voobiy pe awnax iuztami odkiyh yu kxo ahvax.
Key points
Generics are everywhere in Swift: in 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.