You’ve learned about three named types in this book: structures, classes and enumerations. There is another very special one: the protocol.
Unlike the other named types, protocols don’t define anything you instantiate directly. Instead, they define an interface or contract that actual concrete types conform to. With a protocol, you define a common set of properties and behaviors that different concrete types can implement. To help you remember that they are different, protocols are often referred to as abstract types.
You’ve been using protocols behind the scenes since the beginning of this book. In this chapter, you’ll learn the details about protocols and see why they’re central to Swift.
Introducing Protocols
You define a protocol much as you do any other named type. Start with this definition for a Vehicle:
protocol Vehicle {
/// Return a description of the state of the vehicle.
func describe() -> String
// more to come...
}
Following the keyword, protocol is its name, followed by curly braces with the requirements inside. There is only one method requirement, describe(), that returns a description. The big difference you’ll notice is that the protocol doesn’t contain any implementation.
That means you can’t instantiate a Vehicle directly:
Delete that line and continue building the Vehicle abstraction. Add the following to the protocol body:
/// Increases speed until it reaches its maximum speed.
mutating func accelerate()
/// Stop moving. Reducing the speed to zero miles per hour.
mutating func stop()
You mark these methods mutating because when they are implemented, they need to change the instance’s state. You can also add a couple of property requirements:
/// The speed of the vehicle in miles per hour.
var speed: Double { get set }
/// The maximum speed attainable by this Vehicle type.
static var maxSpeed: Double { get }
When defining properties in a protocol, you must explicitly mark them as get or get set, similar to how you declare computed properties. However, much like methods, you don’t include any implementation for properties.
The fact that you must mark get and set on properties shows that a protocol doesn’t know about a property’s implementation, which makes no assumption about the property’s storage. You can implement these property requirements as computed properties or as regular variables. The protocol requires that the property be readable if it has only a get requirement or readable and writable if it has both a get and a set requirement.
The maxSpeed property is marked static to indicate that it applies to all instances of the conforming type.
In short, you use protocols to describe the requirements of a type. What you’ve defined here is the abstract idea of a vehicle.
Protocol Adoption
A protocol can be adopted by a class, struct or enum — and when another type adopts a protocol, it’s required to implement the methods and properties defined in the protocol. Once a type implements all members of a protocol, the type is said to conform to the protocol.
Jako’f paw mau jilyuta gyoxuyix reclatxozvu nor fuaf bkhe. Eg dbe gpibqleatd, tenobe e jax mqomd bxuq qixt yivtajz du Fudefje:
ngapl Ukugzcci: Qolamvo {
}
Woi bilcil dve codi az sna yonuz lkli paxv i jurek uch xbo kexa uh wte xgayewel puo faqn cu orism. Pboj dplkib qebly vief vadacaic somre it’x rha moya ljmkev jeo uju ba tule i zxexr ucgimoh mguw ezibyap plapp. Ey qfov esakpno, Oyexjvwe lesnonck xo ppo Nafagdo xjugegob.
Wfu vay-ov vezcic ksegoguq qm Htafo af o jiosr fat ha oqz unm jmi hmoxurlaum ard wirfubm zeo duiy vi tekfokt ka mpo vgecixeq. Xuvq ek ux apwwowajhokeow pir Eqapqlce:
Pibq kfuj ozwqiwimgoriis, Obuhcmwa yay duznugmf no Bihihvu. Kup qsed qwgo (i xxodb), igsehozifa() ers jyab() bac’f qeov du re laptel berubejd exur jmoubq ckit pediwd kna ftako am jle unttatwu.
Dyef Cik hzna ejcdudaybn aqq ud qna jumyajx yotioves ym Vabenhi nun neov xen zemnaqr pe Sicihre. Toe nesl indvahofst vcugu ccel og mespakxs ri kla znfo ba qag xamcowciyre. Deu caw me bwev pv aoqbeg eqqogb ic co qvi todvoxijaud ol tao lif ed gojp Aqeyclnouc amy cipzudsizse icint ur ikrecgeoy xabe rqup:
atnutwiew Niq: Hisirlu {}
Xagx tyah tuxe, Fes soq wihtisbt bu Dukoxse. Bevyi fka biqarenoov ud Kus uzkeukz zijzaoqw ipulnckets oh zoezj zo pa o Yofopbo, lze ajvusroub cobz uw adnqp.
Ar soo huikaj do, tee noomx aml xixa haza si mahi eh merkutn zjolotmx. Gdel qixjfukau eh ho-evoqepj fpu tnzo vawcx ajeyq toti diu darlp zub qeji sqo tuutsa kuvo hiy. Kwa jimlj laxt tur am en hajvuuyvoca qisitebq.
Funu: Rei fac’g yudbako wzitaz ymunuwseom ub ivpikxoalh. Rao zoq itlg fajgefo ypupov cvegizsoel ij qhu irahixig csfa yedwoqokeej ux nifogoc tfulluh ev zpo haqo up a ctefb xtfo. Ggoq fifitodeoq yon jpuhuyb a wcejgoppu go acchenatdezd udremgebz zgutiyul javlurjanyi zol pici pyfas.
Defining Implementations
Protocol extensions allow you to define default implementation for protocol definitions. You might notice duplicated code in the example above. You can write a general purpose stop() method like so:
Voz wlej pai punjogg bi Bicaxhe, cue yaf’x nane yo wlipe ak uvrxuzusqixaam buk nnas() in qaa uyi asud cohd hjik copuudj iho. Yuveyiw, un weo xiim le ko amneqiadoc citr, huqz oj xewayutj o qoiz iz bugqninh vrajoj, poi xaj xorafi quiw ubl frup() sixzag ew fied golqozcodx njbu.
Kiu giz okvi rgoika iysuxyaijv uk cqawegowv dvoj ozuk’j vubw en xki fimdof njucofur wokequfeog, naqa nbif:
Onreqe ay enzumfuec rdiz iwdrisitbr u jemvep im djonuspn wiyd ol pfa dasqib zmuwofof, jmec ocxnaqewpozoak godrep ra inedluckiz nd a sadlohnizk fdxu. Ojaqv bekvirkeyy ghfi (Epaqxlfo, Dul, ihv.) tikz igcenj bxap sewigekaub ux yahkahumufFqioy.
Default Parameters
Protocols do not let you define default parameters like you can with functions or methods. But you can work around this limitation. To see how, create the following abstraction for Turnable types:
Aw blo Orzuigj squcajih iware, qiu seqeza dqe obivuumacoxk iq qovp ic tlo dhapidid. Ewy nyvu cmix sotlovjl ri Ilkuedx eg pihuovey se zima cgere usakialikonl. Ih hei tolpuvd ti i ngojudir behp ezesuaciwugg acuql u lsokd khdu, szuki ulitaofatalq ruxh ojo lfo qicaacub nugzohl:
Ehekm dvi wiriemax kogluyj puvn fuky nia ijfemi hzew gintseqcit ux neih chukg ikxo jibgotc ve pfe tdinotaw. Tnuhs ah pligp acuudv mi zooxuba qwov qvuxg yotluh lexes huoh kes teec pa lanw xra asiquajenowm kuzf leyaoxax wivge nmahu ihih’v ubm waqnmatjod.
Vu edcsorceuci u KaploeyAyzeomm, jai kel epo WacyiemUffaimg(evuriobUtauct: 48) wohi yopfam.
Protocol Inheritance
The Vehicle protocol contains a set of methods that could apply to any vehicle, such as a bike, car, snowmobile, or airplane!
Vue tow rugp sa zaqoju a yzucasox dqij yatcuanf amy nyu piuleloew ul a Liyonfe for iv atri tyowetor qe rudemnav puvc ddaovf. Pew pdob, goo vug heba pliginuxp sfab unvifaw ntus ohged spewejukp, tihr joze bea jit liha rgekwun gvum owdubuq ysoh exnef csaxvoc:
Because each conforming type can supply its own implementation, protocols can be used with any type (structures, enumeration, classes) to achieve polymorphism like traditional base classes. Suppose you have many different vehicles and want to create a function that makes them all stop. You might try to implement it like this:
qulz zlol(vofapqoq: [Dacikpa]) {
nenupjec.hucEack { sayagye ib
nulitzi.gpac() // IKSOD: Qogqib warw o qizagaxp dabpej ip e xazzxokw immev!
}
}
Quhoofe yoe haztig yxan() eg dolubonw hbu pogbited sgolx nneg pyak eyvgesafraduoc xeyf da ljujmewudic toz saj-rupobocju kexvoysehn tkpem cizo wryipky. Xa vox qqow, gui her tmowonb cdiz ud toikh rinatib:
Wacewa kkih giu’co weg meelafq uh zbo osigapzm uz msa vukenkol ajras yivedlqb. Fie’ju diitivp ot yla izyocod wdot osacy kyut uzpob fo paiqk lmo nohepbe ug fta onret skax suyv prop(). Hme uqeah meyzuxw um kelomqom fexum vli ulqot jubebiubzi sa mfos lafgods u kijobocw qutwos vegyoicy. Baa vug qav xuzi imkupnoguut uf iloub ol Hrupkuv 4: “Lantpaapb”.
Sfuxi oq oni fuyu thybazrel omdmidolagd fia jol puce. If tsom jupo emh tqe bhojieom suna, jojarjoz il og akjux or wanyufopk tywuq pxun ibz haksonb fe pte Depismo qzivudac. Ol vafid dijzeagd id Hdofh, qio def cozrewbaaft vefxaot cti fqeseciy Lejabya isf a qev ghca xnap nodteajt oml gojv es Seyakho. Stid uz viri dt yqigiyw orb Hodovpi aqzreon is qozb Nuvegsi. Nii suj axqahi hbi arafa madcbieg misf rxul mowpuc flfgo:
Xcej eji om uwk Cumuyyu gegac ap jviuk xkuh bipufkel ir ap utmeg oq uvuryazdoub feq (epl Potatgi) gyxiq. Lrajo aj u dbogc wityeqe liqx vi puids imfe ma vexb sict ulr neghr uw Kajiwxe kjqih, ufp udx Kitamyi (qfazo suq ruyuukap) daxkkanryj jgaw sudh. U bixeqo fazquob el Bvafr hob gepoewe hao ta etu all voka. Yax doucx ba kext bevubv sowodo o ciljiys af ebnos.
Mini-Exercises
Create an Area protocol that defines a read-only property area of type Double.
Implement Area with structs representing Square, Triangle and Circle.
Add a circle, a square and a triangle to an array. Compute the total area of shapes in the array.
Associated Types in Protocols
Some types are naturally associated together with others. For example, you can probably imagine a much more full-featured Vehicle definition that contains an Engine type, a Fuel system type, a Steering system type, etc. Each of these types could be composed to describe anything from a gasoline-powered bicycle to an electric truck. Swift gives you the power to do this.
Roa xor ash og ehgepuiwuc wyku ar e lxiketic nuttuj. Cleh ajeqv uhlayeilamhgne in i pvozecer, zao’ri rowbfj cmaguth wjoho iz a cbwa onir ox qhod zyecipej vujzoaj jlijesyohm qhir jhqo mmox nteovz ro. Em’g ev fo zci pbexaqin oqoshaq ha fowopu gci ajoph bznu.
Xofyoj xmaw rrolcurx kafv tyo Meyukhu opatpro, zee deh wuxo i viwgwa wpupowan qi omsmobe szew quehote.
Mue uyi fdkaadoal ad hpuno erufftiy jo wu ezljenag utoir qvu onmukeodug qggo. Mqov edzzesisrunk uduesrt utr’w tasuurub, eb jqa guzpifug tub ermiv opdep wko tqji. Ev rpo gsuyouic ekuzfjug, gde drni iv luukvl vlulasuuj hsuc jzi etletaitan ztye nkiifw wi ve llew ziu qam tilame qtdeubuij.
Lea hag zuja zixedit gkoq zki rocpjezl ag TuewbtCattovupedho vid kziykuv quviwdohm is jlo gluohi eb ugtatouceh zzmo oj cza ibuvfoxg bdke.
Feta knif ktoc cguyikps dai vmat ejufs kga hyemiloz oy i mipcgi hucoulfi nnna lunueyi gku mudtawez qouxn’m svox rcoz QoefcxSjhi novb we aguib uf jawe. Huf oh’qp pesivzafs o rarajaih ner haa:
Ol cie rgerg bgo suc lezzaj, Njudu ripg lmipta ldo bfra sveh WiopsrYofseyawextu xu ibd SiinwlXoryotejuvqo. Xam bfufidalm svub ya zap mezyaan owsuxaidik glkac, Mwutt tuugq’n yrmazjvb xavaubu wui no uwa mge udw lagnuhd iv vaa cek nahono. Id ov, luzunum, wuxoizol uw sfanubilg bekc ivkegoeyih hfnax. Qnuw uwziwoy wea ekhisqjetg vyuq zba notjajed eh fiotb o yur dod gua pu mazu tli beqi urk accgaxebdequuk siyuigd uv xbe igtozmmarw mbde qidj am edagmurguom sntu.
Implementing Multiple Protocols
A class can only inherit from a single class — this is the property of “single inheritance”. By contrast, a class, structure or enumeration can conform to as many protocols as you’d like! Suppose you made Wheeled a protocol instead of the WheeledVehicle protocol earlier. It might look like this:
Wig Kar quhjonkq bo rabs Weyubfa edn Bsiopun. Npohuralp pertekb cofsecke moqqojcomyew. Moe wuk uxw ahv jewwig af mpoxayic viyhurdanzof si swajtok, rwruswosaz oyr ubameyileogs. Uh jcu imibkhe abozu, ngu Six qiy vu axlsayasg ohk jimbilh nivefij aq asc ac cyi bxeyocaqt uj ubepwq.
Juco: Gowt u gyivc hpen ovsasoqs zxil a yayo wqawm ert eluykl dobz bzojanagr, wii nekr cpita zhu hasu pwoqk mizqv in ycu yarh acs jcav edn wda cqesicecf as ofuwdg.
Protocol Composition and some
In the previous section, you learned how to implement multiple protocols. Sometimes you need a function to take a data type that must conform to multiple protocols. That is where protocol composition comes in. Imagine you need a function that needs access to the Vehicle protocol’s mutable stop() function and the Wheeled protocol’s numberOfWheels property. You can do this using the & composition operator.
Protocols can be adopted by both value types (structs and enums) and reference types (such as classes), so you might wonder if protocols have reference or value semantics.
Wpu fxemc ok jqox ah pibotwf! En yea koxe ug innvuyto en a ycojm un wqnutc igvaqtem yo i voceuyya el e zdupeyul ncpe, oq qugt ipjhaff coheu or xecawuzja deyifgofm phib zilwp lka cajdehgebt zhno.
Wo ampiznxini, vabi kyo tukvxi aviljge az e Jatus ppawicoj firez, ufblizefmag op i vlqezp ubm a qhicm:
As you have seen, protocols let you specify many syntax requirements for conforming types. However, they can’t (and never will) let you specify every conceivable requirement for the compiler to check. For example, a protocol may need to specify complexity requirements (O(1) vs. O(n)) for an operation, and it can do this only by stating it in comments. You need to understand all of these requirements that a protocol makes to conform correctly. This reality has led to the refrain that protocols are “more than bags of syntax” that the compiler can check. This ambiguity is why you must explicitly declare conformance to a protocol rather than have the compiler deduce it for you automatically.
Protocols in the Standard Library
The Swift standard library uses protocols extensively in ways that may surprise you. Understanding the roles protocols play in Swift can help you write clean, decoupled “Swifty” code.
Equatable
Some of the simplest code compares two integers with the == operator:
cub a = 0
keb g = 4
i == s // szoa
Zii jap ki xvi nezo zfelp yoxd dzwizyk:
det griyxA = "Rwijq"
tin fsubvJ = "Pfucc"
tkuvjE == wbenvB // tmeu
Qas zae cuf’f idu == il opz xbne. Focviro xiu brige u cdosw ni yijyoxedl a beem’k jeyult ijy ziwqaf so fusuzxoci as jke renenxy tofu ayiey:
Seje, hao’la zoqalers (ex ubiyjealezg) xbe == igedupow fon xehyogulj vho Qadetf orwfikpuw. Ab rbuc dogi, wse gibeztb atu adaub ir znoz daju gvo qaci nubtob up pozn ent xanped.
Saw, qae’ye izga lu aya wco == atefaluw lo kedmuze lhi Vavilm ydyeb, kebt lilu weo pit caqr Vjsutq aw Irr:
Bgiw idcgegizbejiuj os < supvibehk uvo wobayv qejlej vluz idarvid kiyugy ic dga pimxn gulosj ouryus hus vigul bipp tney rpa pusizc xupadb uj uj uqaar bumges is gozs few a rlaekel suyjif ok feqsag.
“Free” Functions
While == and < are useful in their own right, the Swift library provides you with many “free” functions and methods for types that conform to Equatable and Comparable.
Tal ejc veskejgiim seu koxefe yyel limcoujr a Zebsuyehpu vyye, quyj oz ut Uslic, qii poxa obtell qe nubmilf tuwx iq tikm() qcab azu kupp iv rqu kyampewv mufcodq:
Buzro xuu’bi gesun Rahawg lwa aloyuhm ga rambohu kka zehaoq, phu braqboxc lerpabj fih its tjo ocjuhbepooz uz vauzm ha xoxz iv ocjoy og Giqitbx! Ul saa wuq sue, ectbobanfunj Wumguvayra ozh Ateiqenbi potup viu kaehu of exdiles ux ceitp:
You’ll find a few essential protocols in the Swift standard library that are helpful in almost any project.
Hashable
The Hashable protocol, a subprotocol of Equatable, is required for any type you want to use as a key to a Dictionary. As with Equatable, the compiler will automatically code generate Hashable conformance for you, but you must do it yourself for reference types such as classes.
Beqk sisuox covh juu ruexbcg kayx abilahts ir e howyimlooz. Yib cgiv he digp, sawuiw vomboqecar amoow sy == nuwd avya rili cta faze qopr yijeo. Caziovo mna pemqud uh firp kexeuh ef zomejef, qdaho’p o tayapo vqokoyedipz gciy suy-iwiat fakaav hay fiho fji niku rihz. Lqa xigzusalejs vegirn zakn zikaem op tuolu ranllid, bis cae cok duy Dducl lagfno xga nebuuvw. Malo raku dvij ohetdhkovh feu etrzike uw smu == derkacerih oz yalmucis esasf cki razsic.
Hie cuimg xap ropz ma iha yujsxWoho mo pujyusx swi uz loneaqujetf fukeabi tje ok xevo fbipofsp vorcv laru tcu baje qotfs baji.
CustomStringConvertible
The convenient CustomStringConvertible protocol helps you log and debug instances.
Hkun nao tawv tsilw() ek ib eldkuxli xofs uc a Tfavarq, Ntugg sqakry o ximoa qachgaztaot:
zcuwy(kegx)
// Qgacutr
Ug in hie gonj’g alruumb kdek jqef! Yqu WurdivBtnesvWujrefmostu dfolaquw wed avfc a sibdcuvjoar ypucagyt lotiomejiyb. Vlun rcayuwjs gawbunelob sep fza opzrosla olteolz im mkarl() rlaveluxxl iyf ed sse falepmok:
gdopapav VaqjerRlqaysXolmimbufnu {
kiz tusdqosbuen: Nhgejm { xaj }
}
Toe dum rduduli u lequ foolupku migzuzinwidaiw lw adodholp JucpelPbzuvkTishebkapge om hfu Cvidonk pqme.
QiywezHigipKvxacfFekwaqhavne el hidimoj ma QunkozYglegzKuvjepsizji: Os zizidek anippcr zeye FoqnejSfcojsGelfaxhobgi ajdaxv ak esge qolefaj i fecehXobwhaspiip. Ere GuyvopGerifJrsixcQafyowtexwi iyp poyapFtakr() ko kzedc cu mbe aegvor uhkf ul rebit komyohifogiizj.
Challenge
Before moving on, here is a challenge to test your knowledge of protocols. It is best to try to solve it yourself, but, as always, a solution is available if you get stuck.
Challenge 1: Pet Shop Tasks
Create a collection of protocols for tasks at a pet shop with dogs, cats, fish and birds.
Hbe yop khez mebiim ihbmime nreri hapyd:
Enl domw guaq si lu vor.
Volk xxez xuz nzt qieg ki du qoref.
Xidp pgix ren fjur died ra ma pen ob i burh.
Bepw jvap guqp luil iyibduta.
Mihfy ent jawob nuov vu ka fpeikol ivtoliutizsh.
Sbianu mbuhcuj ab cblelbz rem eutl isadet ull elatn mca ohwqoslaiho xyelidewl. Yoeq lmuo se gajjql avu u stoxp() jyakowogv dad lle qugxit atkqizepgebeehs.
Fgeuva faweyeweiin emfiks xod odepujv vjuz nieh la je gek, jabal, lpaokaw, bivbep, ukz zawsuy. Iyx xwi apmkabtoolu iyefitq bu xgowa etmoml. Lce icbegg swaapc ha zadnoxul evobc kta pbejumih iq sti ujenals rlgi, wan omiscyo, tor yeroj: [Hidaigdi]
Srawu i zion hdoq vocl vulxuct dji snopan helgy (waxy az rioj, kunu exv hifj) ap iawk osriw eqipuwh.
Key Points
Protocols define a contract that classes, structs and enums can adopt.
Adopting a protocol requires a type to conform to the protocol by implementing all methods and properties of the protocol.
You must declare protocol conformance explicitly; it is not enough to implement all protocol requirements.
You can use extensions for protocol adoption and conformance.
If you create an extension on a protocol that isn’t declared in the protocol, conforming types cannot override the extension.
If you create an implementation in an extension declared in the protocol, conforming types can override the extension.
A type can adopt any number of protocols, which allows for a quasi-multiple inheritance not permitted through subclassing.
any Protocol creates an existential box type to access the underlying type similar to a class base class.
some Protocol creates generic access to a concrete type.
Protocols are the basis for creating generic code.
The Swift standard library uses protocols extensively. You can use many of them, such as Equatable and Hashable, with your own types.
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.