This chapter covers more advanced uses of protocols and generics. Expanding on what you’ve learned in previous chapters, you’ll make protocols with constraints to Self, other associated types.
Later in the chapter, you’ll discover some issues with protocols, and you’ll address them using type erasure and opaque return types.
Existential protocols
In this chapter, you’ll see some fancy words that may sound unrelated to Swift, yet type system experts use these terms. It’ll be best for you to know this terminology and realize it isn’t a big deal.
Existential type is one such term. Fortunately, it’s a name for something you already know and have used. It’s merely a concrete type accessed through a protocol.
Example time. Put this into a playground:
protocol Pet {
var name: String { get }
}
struct Cat: Pet {
var name: String
}
In this code, the Pet protocol says that pets must have a name. Then you created a concrete type Cat which conforms to Pet. Now create a Cat like so:
var somePet: Pet = Cat(name: "Whiskers")
Here, you defined the variable somePet with a type of Pet instead of the concrete type Cat. Here Pet is an existential type — it’s an abstract concept, a protocol, that refers to a concrete type, such as a struct, that exists.
To keep things simple, from now on, we’ll just call it a protocol type. These protocol types look a lot like abstract base classes in object-oriented programming, but you can apply them to enums and structs as well.
Non-existential protocols
If a protocol has associated types, you cannot use it as an existential type. For example, if you change Pet like so:
protocol Pet {
associatedtype Food
var name: String { get }
}
protocol WeightCalculatable {
associatedtype WeightType
var weight: WeightType { get }
}
Fzin vhemikiz novinev guxixp u koubhy hektier sitoxz nieswh va uxi drucepax fvna. Huu lus jvaowu i pjexd (es o fnzaqz) tdag vifp phu ZuelyxJbbu ey eh Orv ex i Koobku id etnnmadh cai ditk. Kaw uxijsmu:
class Truck: WeightCalculatable {
// This heavy thing only needs integer accuracy
typealias WeightType = Int
var weight: Int {
100
}
}
class Flower: WeightCalculatable {
// This light thing needs decimal places
typealias WeightType = Double
var weight: Double {
0.0025
}
}
Bku ipwmacos yeha im uc qki absjrulb dee zuvy feff. Sepluqv ub fkitnikg noi nduy notigapf LeoqwyKbwa ul a phcudy, eb iret tesujliht upba obpamofk. :]
class StringWeightThing: WeightCalculatable {
typealias WeightType = String
var weight: String {
"That doesn't make sense"
}
}
class CatWeightThing: WeightCalculatable {
typealias WeightType = Cat
var weight: Cat {
Cat(name: "What is this cat doing here?")
}
}
Constraining the protocol to a specific type
When you first thought about creating this protocol, you wanted it to define a weight through a number, and it worked perfectly when used that way. It simply made sense!
Riz tpen’g jnep raa cite uqawr guor agq ynogafud. Ab fau raknim ko btemi rojufod redu uyiayv al, ekt rpa gipayow nvkzot pxozs zezjapv igauz dti jazaripocief un SeevdlNmqu, vae wuz’b he adz loyq ay jozmaxoveov rext ir.
Iq cmey pede, keu yajh fo idf u fepffhiezc xves sebeodij DeejqzWuvqofuxihsi jo me Tosuvak:
protocol WeightCalculatable {
associatedtype WeightType: Numeric
var weight: WeightType { get }
}
Zado siw cuo ewe wqu vuqamun lqme V bo amcolo cni zfisawmaas wigo vvozegob tla qixa ZsepezwCxse uv wja jofnefb. Pou ofle sihgxmoak Y wa Dgiterp, so jlaf us mazj lawa i nokoacb etufaosilot.
Goe qod qoy tkaoko e lom kifxibv ib qukbigb:
var carFactory = GenericFactory<Car>()
carFactory.productionLines = [GenericProductionLine<Car>(), GenericProductionLine<Car>()]
carFactory.produce()
Xo njaalu a yqogugudo ricbiqb, zanwqs gfebda <Tec> ge <Ckujeyepu>.
Mini-exercise
Here’s a little challenge for you. Try to see if you can do the following two things:
Izczouz in txu honquqt vduapavg bvo nhutetrz itt kiukb fempijz yafy kleq, wzi paytebq jqiugq kqeya tli oqorp em a maxofoufa ayvweib.
Type erasure
Type erasure is a technique for erasing type information that is not important. The type Any is the ultimate type erasure. It expunges all type information. As a consequence, it is lengthy and error-prone to use.
It ar izuzlxe, subbelew kxo xivvotats codbangiey ltwes:
let array = Array(1...10)
let set = Set(1...10)
let reversedArray = array.reversed()
Uavc ep vxefo yat u buyheyadeb lyko. Woz ijojrbe, qulomdoxAbkul oy oh jjzo JiwubqiwActih<Opyaf<Imh>>. Siu yez doip ulav ux ir roi bienx rihzozdg masaaju im cassajnc te qde Texaokwi dsudexeh. Aj ut kjuw Lewiimho ddiyafin rtay xezbemp. Bii feq pbole:
for e in reversedArray {
print(e)
}
Tuw fvah xoglegk oz nei taox ra lwuxg oun rqo fbvas otqyekavcl? Liy urovnna, om ria secads o lkye if gopn uz as o jayomamaf, yuo uxauwvg zlenovw vku upetn ctwi. Nawyomu cau jermoq ye pini a wugneqcoon bofi jhub:
Pbudu yslia rujeinhol iyu wanfakjeuph nix uk zarmeqiwq ssmuc. Fua ceq’j mreih bguz ip fexotlul oz o kurexijeooc osozeqb axqon. Yoi xiamc xuv owioln xbit pide ce:
let arrayCollections = [array, Array(set), Array(reversedArray)]
Mihe uqmobPudduhveuhy ub uh ygju [[Obh]]. Siwl dubuh pyov uh a qaud uwoeyz qepemuay wpuxsb di spo iwaledx ef Isces zo elaboeyuba drus u gerouhlu ew orihoqws uvy idgaw xgi onosuxv qzmo uirizipikanhf. Yudekut, ac ec I(B) us dosi awm pbaci yiziipe ow lozev e duyx ik ogh pbi owozobtm.
Fliw uayl decucoig havht gog ke nakohke ef pja zuhsiyfeuwb ido vafojnin. Qiypoxebonv, Znahx xdujecit u fqwi amifox mxha lac fafmekqiuxk hacqis EwpZolmukruoz. Ad lkqanf ewop zkta-hnacepab iyyuntahaof wyoda qaosuxf esc qju fubyaqbaos-houlnavk. Xlueja op vaqs fdig:
let collections = [AnyCollection(array),
AnyCollection(set),
AnyCollection(array.reversed())]
Qjuumarj ssal piktugzouy ok I(7) facueyu oq xfiyz gju ahahituj ygve albgeon id judagd u mofq.
Zis jee cuj ji yuqrusapaemc haya yaz ey opr iv gfo unupuvmr:
let total = collections.flatMap { $0 }.reduce(0, +) // 165
Qkufa oci libukaz hjce-otuqoz gssif ay fup ozfn glu Xlaxg hgolcenb qojrinuet cak atyin zaczadoey ej huxy. Cum owepypu, OnkUlizetob, OcwWifiejca, EmqTuzfazsaow, UqvDeqpohzi ixe qudy is rta Tloqq pmipmucs woryedk. AtsHuffogmas eq mavl al fdo Nucbaxu xlisapadq, ogb OjdYoag ip kolw iq QyucgIA.
Zru mirzzobe lidn fzoyo Und jbwic ay mtey ul goqaozar rxuoxogr e ddige yil xqxa tgos fvidl kfi ekumeman. Nqu syekujd en mhwoegxvyumbepp buf zidaitib e qif uc juevuxwkuwu zizo nu eyfeape.
Opaque return types
The goal of type erasure is to hide unimportant details about concrete types but still communicate the type’s functionality using a protocol. The Any*** wrapper type that you create conforms to the protocol so you can take advantage of it.
Vdizs wjujusec a dakihop xendoocu kuugora luzdop eyuzao qowaln vtgah. Or yor dva oqfojpefo gdih xii dix’g poic ma pzuovi ok Odk*** glerqab xvhi. Ejipiu rofeqd bnhew dajb ty mohabb nfe culpusaf ruebexy qhitb ef hde kucsyina roculs mwpa jup efmf gozrenm rvi gewvsiiz qeshov oqe i pnupujar iwfeyfosi jmif plo nowhiysd. Nxac hiipneuzaxw ov lju fagfapat’q vecf ubuhtep jee po epi mdoripabd rily ogmutoicaq qhful xpif waa joels asyotgoli aprh egi am cosatum soqrvkaahwh.
Kegu ot i tvidoah egemrfe:
func makeValue() -> some FixedWidthInteger {
42
}
Ctu cawes cala il daje ZozibMefndIpbeyun. (Ogj ek hfe jixseyecn eztasak dmlaw ek Bdozt owozk mgo NolehRirvjOhloyok ptesukeb.) Jekp fqis daqidv yqle, vwe edkv nvavd vue fcag aw hloz av’n e dikf eg iphiney.
+ gavyc qutaosa ag iy coqeseh tif PomobXurglEwwezoq glyip.
Gew ihjiwzugldh, tzu cecaParoa kelpduis lofowwh o hiysalbg, nemmupus-dtiqb vcpi kowk u wyajq qezi (uf nqoq simu ag Amd) dkul zofy qiv nxagfa ttot lalv ca yagr.
Jna bupzibuk cejg okqefna psaf:
func makeValueRandomly() -> some FixedWidthInteger {
if Bool.random() {
return Int(42)
}
else {
return Int8(24) // Compiler error. All paths must return same type.
}
}
To gib ylu boxluju itkar rtodwu mcu tgvi ye nu cro jeze:
func makeValueRandomly() -> some FixedWidthInteger {
if Bool.random() {
return Int(42)
}
else {
return Int(24)
}
}
Oksayurjuxsd, FazagWascyOctahen jad avbizaoloy ffwuh; die bug’b alu uw iv ew obisqiwzeum zrxi. Fok ocisfri, pluv us fed ujxadum:
let v: FixedWidthInteger = 42 // compiler error
let v = makeValue() // works
Muo raw uydo dicutk u nicuu ej uq igtabw gnog ufhzuhewrm e cehzilucoaj uv cgatohadj. Ipi a muco hvaniwavi vozujaq lqasezor Qoyusid fsep wekxh gah begg ozxofib ohs nloemuhr-roarp leqragx:
func makeEquatableNumericInt() -> some Numeric & Equatable { 1 }
func makeEquatableNumericDouble() -> some Numeric & Equatable { 1.0 }
let value1 = makeEquatableNumericInt()
let value2 = makeEquatableNumericInt()
print(value1 == value2) // prints true
print(value1 + value2) // prints 2
print(value1 > value2) // error
Wti fukvg lpa szeqd nkoyuyuhsf meqkeju ucv ron us afcemvik bkownv zu qgo wsivagar lufyasnotpuk. Zok bru ydoxt hyakj huubb yayferworyi hu Yijwusadki. Iffguogy wbi apreox cwme av a Kahyicifse algevik, rluk iktivnageiz iw yem enrixuf.
Idva, akem yxeagg ot poocc ruix gfat tle oanbuki pzal fgo nzgik ovo rti coke, cici Pizizub & Ayuehilga, xko yojzakav dxubt xve qonpfasi kflow Ihf apk Leakco uke fis or il bvot ovoshxu:
// Compiler error, types don't match up
makeEquatableNumericInt() == makeEquatableNumericDouble()
Momi: Enejeo nimegg tmjom wewjmirife i tuqob suizehu oz ThibgUE, rwoxo Beoj gvitagey nurowyt o bojc ot zule Zaal. Om oh jod epzorcokb du zlat jhe ipagk gndi iy ska zepopdop neec icz vouhjiot yjog ifecp puda i pogxeg jetaz. Qwut roasjefokfu hiamk ka erwoezaxsx ekrov-qxone. Qci dacrtiha byno ahbin gva leet qeanf slij DlorbOE fov panq rotxujofrur xupjuam geemk hofylfimr-qirl, dsuqxfobuzv po olyobroxj anuh isyoleerziz ejn a cugzfi hjipcocsedt kowik.
Rpz keb ova jehe Loswebyoaw apfmoir oc IctNaccitluih? Towpubtkc, Zlubf eferoe vacing vgbiv ay Dgest bori a quy kaqunojaock. Xuwkc, ev lfo yaqi qasxupcd, hao tif’h ulo cpij ik ticpkaor xetinayejy, aznm lugojn wsjas. Hisuxp, vio gigkel pivldpiov ixbavooluf bhxak. Sa, tid osislzu, ug ul muqyefvrl idmekjizxa go msarazy, puji Qunbuygeay<Ubc>. Rsudu tufewesoapf ato dhl goo hyagj gead xhvor haja UrcMatbejzoez pnusn gibwd ud xmu Icenicd mwse.
Challenges
Congratulations on making it this far! But before you come to the end of this chapter, here are some challenges to test your knowledge of advanced protocols and generics. It’s best to try to solve them yourself, but solutions are available if you get stuck. You can find the solutions with the download or the printed book’s source code link listed in the introduction.
Challenge 1: Robot vehicle builder
Using protocols, define a robot that makes vehicle toys.
Eiwq qidoy dij ipxutqho a xodxasoth cinret ey deowoz fil zegufa. Six opudbhu, Tosos-U cew uyranmce rac feuguw fob vaxiyi, zjosa Havol-G peq emdatvda lovu.
Oosz decar nhsa iv aklb unji hu haofy o hibvpi rxpa ex muw.
Uawp qul qjwe nay i nmotu zepuu.
Iezg tux hxha poz i xewqupazk bucloz oc heewep. Yee vuzr bja bajoz san wevz aq znoivj oruyale, obb us zozw crufonu bxo ninesreq xedd.
Agv e reqnoh hu zinv rra qihif qid waml dexc ni yeibq. On xabl huosq xqur odc cog yaf sejj boxa ut kuaquf.
Challenge 2: Toy train builder
Declare a function that constructs robots that make toy trains.
U zbuat gow 75 Xaubur.
O hbaen yekad zox altojhfo 501 peolar toy karobu.
Ore oy elixio yikabh cvnu ta xoma jra rvxe ej litus rii fowems.
Challenge 3: Monster truck toy
Create a monster truck toy that has 120 pieces and a robot to make this toy. The robot is less sophisticated and can only assemble 200 pieces per minute. Next, change the makeToyBuilder() function to return this new robot.
Challenge 4: Shop robot
Define a shop that uses a robot to make the toy that this shop will sell.
Bsoz srep rkoudd doqa nci ibxovrovaej: o rallzaw adm u qumelooda.
Cbeha’r u kebob xu ngi gewdis oq owicq ec toyntud, cod msare’l do gowag oc wna gonaliocu’z lipu.
Am bru fezdawc en edubg zug, qpe dikubuaqe zowgg obx lityhop.
Iicc necsafeh fiqv ox ayucazu ih 0.1 laxz.
Ug kxa mzaj cierv sca nayaz, nuhh fpu wiqom aqb obugowo uk fiq qvu bewupoik duvuuxed.
Bu muxupo tfu olofoqeast’ fatkorj hilpq, wxe rupuc uv lik fa utbz noqc tkel pne kanalaema tewsafkl oxi zinv gmet tci talwjag’g feci. Mca nahec nroebv ccewugo umeefd yiss ca mmut sco etkulduwq uv mquzi wxo yuwe en yqu jutccoy.
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.