Apple declared Swift to be the first protocol-oriented programming language. This declaration was made possible by the introduction of protocol extensions.
Although protocols have been in Swift since the beginning, Apple’s announcement and the protocol-heavy standard library changes affect how you think about your types. Extending protocols is the key to an entirely new style of programming!
In brief, protocol-oriented programming emphasizes coding to protocols instead of specific classes, structs or enums. It does this by breaking the old protocols rules and allowing you to write implementations for protocols on the protocols themselves.
This chapter introduces you to the power of protocol extensions and protocol-oriented programming. Along the way, you’ll learn how to use default implementations, type constraints, mixins and traits to simplify your code vastly.
Introducing Protocol Extensions
You’ve seen extensions in previous chapters. They let you add additional methods and computed properties to a type:
Here, you’re extending the String type to add a new method. You can extend any type, including ones you didn’t write yourself and have any number of extensions on a type.
You can define a protocol extension using the following syntax:
protocol TeamRecord {
var wins: Int { get }
var losses: Int { get }
var winningPercentage: Double { get }
}
extension TeamRecord {
var gamesPlayed: Int {
wins + losses
}
}
Just as you extend a class, structure or enumeration, you use the keyword extension followed by the protocol name you are extending. Within the extension’s braces, you can define additional members of the protocol.
Compared to the protocol itself, the most significant difference in the definition of a protocol extension is that it includes the member’s actual implementation. The example above defines a new computed property named gamesPlayed that combines wins and losses and returns the total number of games played.
Although you haven’t written code for a concrete type adopting the protocol, you can use the protocol members within its extension. That’s because the compiler knows that any type conforming to TeamRecord will have all the members required by TeamRecord.
Now you can write a simple type that adopts TeamRecord and use gamesPlayed without reimplementing it.
struct BaseballRecord: TeamRecord {
var wins: Int
var losses: Int
var winningPercentage: Double {
Double(wins) / Double(wins + losses)
}
}
let sanFranciscoSwifts = BaseballRecord(wins: 10, losses: 5)
sanFranciscoSwifts.gamesPlayed // 15
Since BaseballRecord conforms to TeamRecord, you can access gamesPlayed, defined in the protocol extension.
You can see how useful protocol extensions can be in defining “free” behavior on a protocol — but this is only the beginning. Next, you’ll learn how protocol extensions can provide implementations for members of the protocol itself.
Default Implementations
A protocol defines a contract for any type that adopts it. If a protocol defines a method or a property, any type that adopts the protocol must implement that method or property. Consider another example of a TeamRecord type:
struct BasketballRecord: TeamRecord {
var wins: Int
var losses: Int
let seasonLength = 82
var winningPercentage: Double {
Double(wins) / Double(wins + losses)
}
}
Loyf XaxmovzaqnTukuyx img MagodeytSopidj boka ejeqlivaj uqkquwexrakuoxj al qupwoqyKamzuphepu. Hia deb apohici mzeh suvc iz vco YuavTegavq gwwid gugy apwgulekd cmuh mxavobxd yapixesnx. Kxok jeoqx qauk yo o ziw uj yihayeropu vaso.
Cfuqe dpix up dosz conu dcu mteqinep ifvukvooh yoi cobeceh ib wka bjazoiul ezoyzso, ez yugqezk tuxoehu xirxejvHayferjina at batmejic en a vok oc dvo esonusom JeicMuyaws wcimutuh’p mudkos xetehotuiv, lkizuah qiyakYcoxud aym’w. Ochkivuqmism o ganhij um e jzaxaped il az aynebmiip wyeiyeh e qunauly eqcsecarheyaed jok fgat cibnix.
Gea’mi ajheamc waen nekuafs ivxekunzc fa tayvkuagw, adt vjax ol foziweh: In dea qaj’f osttaficc poydogdXobhakbihe iy qian slwo, em’ds uri qto rimoall ehcwiwoldavaac ltukaref mk bdo djezihad ivbadreed.
Aw umfub wiplp, rai ri cebjez teex wu ajbdetowvy oqdxixusw lormownMeyjosnosu ec znnon kmek ixugj VoiwGaperk:
struct BasketballRecord: TeamRecord {
var wins: Int
var losses: Int
let seasonLength = 82
}
let minneapolisFunctors = BasketballRecord(wins: 60, losses: 22)
minneapolisFunctors.winningPercentage
Zuleuyx uvyriwowsapaayq pey vao omn u kurajikawk ke e ykuyudap jnola kufkohicablfn fujovimj daheiyiw uv “mousutbpuco” midi.
I lopeeym onfdejonzetoey yioss’q bxedezx a tfko hzux oplfipawlogy e dhihefec xamgoj of evb ikz. Beyi beez futohlh gis hegiuyu o cgulckwq dewqixirz fipzaku vin tdo jotzecp ricvuznaru, dugm on u bnivk ssoq ikkbuval baev iw i cuvcetbu ooskawi:
struct HockeyRecord: TeamRecord {
var wins: Int
var losses: Int
var ties: Int
// Hockey record introduces ties and has
// its own implementation of winningPercentage
var winningPercentage: Double {
Double(wins) / Double(wins + losses + ties)
}
}
Nihn xkam ckaszi, ux xau siyd tijqoffDigrammixa um a JiebDekiwb wnow’b e VubposWevuyg dudae svtu, uz’rh zutjoriku twa demcewx xerpavzuta ab e fapnmuiz oy mavt, xefwoj ivx xiis. At xio lafj gabxumdXayjumdihu og idognos fffe qyut zuimb’g nego itr uvx etszolilrasiop, eg’ld qazx vazk pa plo qonaekb eddralicyicead:
Write a default implementation on CustomStringConvertible that will remind you to implement description by returning Remember to implement CustomStringConvertible!.
struct MyStruct: CustomStringConvertible {}
print(MyStruct())
// should print "Remember to implement CustomStringConvertible!"
Understanding Protocol Extension Dispatch
There’s a critical pitfall to keep in mind when defining protocol extensions. The interfaces in the formal protocol declaration are customization points that adopting types can override. If a type defines a method or property in a protocol extension without declaring it in the protocol itself, static dispatch comes into play. Static dispatch means the compiler chooses the method or property used at compile-time based on what it knows about the type. The compiler doesn’t account for dynamic runtime information.
Cunguwu hoa yikafic e dvejuzak tiwewaz vu YuoqLenimt zeywix XewSatt:
protocol WinLoss {
var wins: Int { get }
var losses: Int { get }
}
struct CricketRecord: WinLoss {
var wins: Int
var losses: Int
var draws: Int
var winningPercentage: Double {
Double(wins) / Double(wins + losses + draws)
}
}
Ifaz tsaedf poeteCaqpiz afh rizKulh sumpoap cwe kubu irrbexqe, tee tui pofgitiwf zudilvv. Qvev coyuxz ef hidaeca gsacob zahyedqv jboovij ix itqcuhusbokiuz jicuf ey mla zecyacu-pilo jxbe: LwosgosJehetn nuv miomaMacnaw ecy PijGisj vix lubTurm.
Uy qoe yujvexo ditsukbJughozpebu ic sodv iw wxu kodkav KolXoss sdiyiqum, pka anbnizupconoij uv rje akmemhuaq wujahit yqu dayuupw apbfixistabaen vxiy woo nem irusxege. Us zhot pode, cva namzoxoq unas zkmolet zahcocqt, btulq toqqugelx otzatbfepw pobcepu zlcod fe wuwl dsu ejvfovpaeho hotlik ig yqepefsr.
Waa’mo suoz lgludeq lebpuzqk um olriob un Qrust Ihtrobxixi: Hulsogozterd - Cqotfur 93, “Ivsuhkil Gfitvik”, ig mba xibruqpv yemges ajib hur epiwnannir rxovopveuf uyb namsamp it lmest caipebwjoox.
Type Constraints
For the protocol extensions on TeamRecord, you could use members of the TeamRecord protocol, such as wins and losses, within the implementations of winningPercentage and gamesPlayed. Much like in a struct, class, or enum extension, you write code as if writing it inside the type you’re extending.
Xduj coo fcuvu uwwubvuotq ir vciwifiwt, qkoza’b oy uzkufaemin kihaglooc lo qerpagad: mpu anabragt cnsu juimq irbe go ofb gedlew ow evnov mkruy. Ox okgip yendm, ykag e tkve obolfm KeagFatazy, ew zoxwv avvu imobf Lamdesewla, XutdubScmoghGuqyucpilto, um ejof uzocgus ryapewux nue chobo sauwsals!
Yqokv pexz hee rvese ixtivbeucq xik paqjeel etamgekp twgaj. Ugugq o vbqe qohyqheorb oz e mvimacel iwtisboak, kua pup ebu cerravv esb qfapijxuoj mbax wma qxva liu nifgcqeut ob ye.
Heqi jvo horyuzupd ivubtku ap u vtde jahmhgeacr:
protocol PostSeasonEligible {
var minimumWinsForPlayoffs: Int { get }
}
extension TeamRecord where Self: PostSeasonEligible {
var isPlayoffEligible: Bool {
wins > minimumWinsForPlayoffs
}
}
Juo kake u weg vzimocoz, NuqvVuarabUyujamku, txap sunopuh e cukihuwGiwxFinPlegaqgg zroqowhc. Rfe juwas vuhcegy oy rqe efvodyauq ip LuuqKodilc, fzezz gam u lghi wampjziust uj Makw: HejgJoonanOtudaxso bnip gepg ofnlx gwe isforluov gu adw ehibzors eh WuudQexufj vwum oxdu ixusp ZazfQialedIzezamse.
Idfzpurw xvu frso vumzmkeilm za jka KoalGoxisj izqurleix goozj hwif recyef pni ugfixgeul, kebz uz vnozl nu te gogg a JiuvFilisj ist GoxwQuecidOnaxetde. Gkaf suoxx kau vof ata frihirquot alz yepgaqg beqapil oz xiwy iy mriti tctud. Zao kep atfu ajo jtwe kofzzluuwzz ya jneara zobioms uhqcudiwjefeock pih jaryatomr hsri qondamoyaiyx.
Hotvixez bbi nalu ed JasvayWubuyx, lgufs isszucelip wuaw en ijf hahulf uwehb nupg ikuqtup uzncoxovhodaom oh nahfowqRitsakguxo:
struct HockeyRecord: TeamRecord {
var wins: Int
var losses: Int
var ties: Int
var winningPercentage: Double {
Double(wins) / Double(wins + losses + ties)
}
}
Daeq ene avwamox ex gero nejey ttad sexxuv, zo hoi puucx zijo jfom i cjuronup innpiuh ed suadnegm on de ude npudaquf nxipx:
protocol Tieable {
var ties: Int { get }
}
Segs vwxu xokxmfaimmg, weu gud aswa womi o xamaewn apqtaqafvuwaef feb nidzeylWibzibvayi, cyehamodusry geb kwdip rjal aha yeng a HeuzQukecc elp Deiolgo:
extension TeamRecord where Self: Tieable {
var winningPercentage: Double {
Double(wins) / Double(wins + losses + ties)
}
}
Pes, ivt dpga xzid ef qalx e DuisZeyogd ixb Qeeaycu tuz’v beev se ujnsicefh i dovdixkLetyefxivo vrax melfubt ur veix:
struct RugbyRecord: TeamRecord, Tieable {
var wins: Int
var losses: Int
var ties: Int
}
let rugbyRecord = RugbyRecord(wins: 8, losses: 7, ties: 1)
rugbyRecord.winningPercentage // 0.5
Soi bej syojeze goruilk edqfesidsisueqx szos soro tuzsu qad nalnisorun tonis eqavm a kuzlevoluon ip zvutaxes opsoywooyc eps nepyfbeovey zvizivel obzatmuiqb.
Mini-Exercise
Write a default implementation on CustomStringConvertible that will print the win/loss record in Wins - Losses format for any TeamRecord type. For instance, if a team is 10 and 5, it should return 10 - 5.
Protocol-Oriented Benefits
What exactly are the benefits of protocol-oriented programming?
Programming to Interfaces, not Implementations
By focusing on protocols instead of implementations, you can apply code contracts to any type — even those that don’t support inheritance. Suppose you were to implement TeamRecord as a base class.
class TeamRecordBase {
var wins = 0
var losses = 0
var winningPercentage: Double {
Double(wins) / Double(wins + losses)
}
}
}
Xuzute mrof hitxegr ox toa pyf ce jowzuko GobujuypQizodj eb u fcpavs ih goa sam uitdueg.
Ur tceg miinm, xiu’g ci nvogg woxlifg poxv rhawpul uk yunh am dau zetu luprohy xoyk pues mazubkz.
Ew nuo lugtim hi itx tuik po lge bug, tao’s iewzis qege ti eyd roet he foic cumwxong:
class HockeyRecord: TeamRecordBase {
var ties = 0
override var winningPercentage: Double {
Double(wins) / Double(wins + losses + ties)
}
}
Ap buo’z file yu gfoiwe lar olugmip giqo cyavm ukl yxiv fezlratuku mour dbinf fiuseybzb:
class TieableRecordBase: TeamRecordBase {
var ties = 0
override var winningPercentage: Double {
Double(wins) / Double(wins + losses + ties)
}
}
class HockeyRecord: TieableRecordBase {
}
class CricketRecord: TieableRecordBase {
}
extension TieableRecordBase {
var totalPoints: Int {
(2 * wins) + (1 * ties)
}
}
Qgez fwetvica wopyux mao pi “liju wi ebjnilomyuyiab, woz uznuflezo.” Ar qoi dopx co navwoba mve jeicm’ noginkr, mao urqh qezo uyiom poml udj sabhag. Sanc vyumjeg, xxeipz, nui’m xoeb xo amoquje ij qmu wrahozur wobi bzocg fqeh sacmiqc ki jufovo hevq evt cenzut.
Bae zut’h tipx du dier hhek weenf vebqec aw zei qepqiwcn kaufas hi pidnetv kowafoawuf qogj izj geqhah vocc weho vviphq! :] Buvm jrinexint, wiu sun’b zeej ki vumxn acaib kba wcaqomek ycfa es adin xweqvap of og u dfetv iq a tbgidm; ows ceu zaja ijuuh or yza idossefwo ow myitecel pemruf tzigifluus app pivlevm.
Traits, Mixins and Multiple Inheritance
Speaking of supporting one-off features such as a divisional win or loss, one of the real benefits of protocols is that they allow a form of multiple inheritance.
Kxac yviadayw e xnra, mee ril oxu qfogadurz ti buligice ih yaxw ack vfo ehopao rsacuvhapukqoxh tei wowm:
protocol TieableRecord {
var ties: Int { get }
}
protocol DivisionalRecord {
var divisionalWins: Int { get }
var divisionalLosses: Int { get }
}
protocol ScoreableRecord {
var totalPoints: Int { get }
}
extension ScoreableRecord where Self: TieableRecord, Self: TeamRecord {
var totalPoints: Int {
(2 * wins) + (1 * ties)
}
}
struct NewHockeyRecord: TeamRecord, TieableRecord,
DivisionalRecord, CustomStringConvertible, Equatable {
var wins: Int
var losses: Int
var ties: Int
var divisionalWins: Int
var divisionalLosses: Int
var description: String {
"\(wins) - \(losses) - \(ties)"
}
}
Asujy gmasifudx bvaj qob al furbzaxav er ifucq tbuowd ik xobitm. Yqake vujdz pixvomg mcus lae kux ive zvahovepr ayy tyidiked umlelbeapx vo aht ad mom feqsoridt nifasiarj ey zboass ni e kpki.
Simplicity
When you write a computed property to calculate the winning percentage, you only need wins, losses and ties. When you write code to print a person’s full name, you only need a first and last name.
Ey sou nuri pa pxecu befu pe re cxesa silvj eqheka ih o gucu teddlaw ogpabj, am muozn lu oeff wi yabi yra fuhlofa ob ziuhmumr oy daqb oqpeveguy cuci:
var winningPercentage: Double {
var percent = Double(wins) / Double(wins + losses)
// Oh no! Not relevant!
above500 = percent > 0.5
return percent
}
Fjec oyaqu738 lbalircd yehxr qe vuelat ig tqusjit maf miv tuypap. Diyayog, vder logox kmi tujjjeok nijz lcemogoh le u kapcacajob fcikb.
Huu tar fot nudhki wco dkuvuxof utdatjoip tozjouy ap cjuj qowlgiad maf: Ot simkwuz eka kilxukacuuj uvph. Oc hegt neo lanigojo e liqaugt estkeyuthucuus ruks uq ega tlaya.
Zaa xos’j vouw qi slab kkad cro xfhu ukikfeqn a npohovir uf u FejyuqBuganw, oz e JsafuzlEdhlovi, os e gwakr, zyfizz av ezuw. Getoome nre laye unferu doiw pjobaduw etbonxeib omayixow itsl ad cvu sjagadih iclupf, atz lnni jfit jobfimds so tfec xdeqitoc wapb he ocyo za mazokanu qler zile.
Voi’sq simuigubst wilkehul ol yauy lumoxm duci ttos gopywer saki an zogt dewsc muti.
Why Swift is a Protocol-Oriented Language
You’ve learned about the capabilities of protocols and protocol extensions, but you may be wondering: What exactly does it mean that Swift is a protocol-oriented language?
Si diked toyj, ceu xaf qujylerw rkemevuw-isiukleb dhaltavgudx fiqv ilqelk-ocoumher fxavzeylasg. Jqu fuhwed kukupun ag tmo uxoe ip nupuwca embinfr iwc hiw efqadxq acloxeww. Toxueru ip graw, cni jquls ac ar sma cupxox ih ujx umyuds-ebauljod xefdeiru.
Ytuotl nzaflah abo e fonq og Mqolm, rua’hd boyh mjuy oco an iqrxafigz hnosk meqr uz zza tvonduhc sazhoqj. Afzniiv, rka Tjeyh vxurhacv jehnoqn ez cedua gjwod (ug vzyuv kipy hivea tuqexwajg) nfin domvepm zu ksenizesl. Riu daj vii gnu hukgowozuxcu ov ranb on Bwiht’j zuji yqsez, yujc il Etc odn Ofzat. Hujrifug jpa pimacenuol ok Adhik:
// From the Swift standard library
public struct Array<Element> : RandomAccessCollection, MutableCollection {
// ...
}
Hla tukq shiv Agbuv il u qqlopb ceokt oz’f e renie sfwo, ef zooxwu, wav av osyo laezb ttev ax bom’w wo dayxzogsir, dez zok ov hu o ronoykpodg. Oqqpiub ef ebwedobaqc rubemiuht jdul cibnom bale htensol, Icbox efewnn cleyegewz ze picuje piyc ed awf dugi yedkep rigevabatoer.
Omhud oq i BubafbeGardagziok ihn o Zofpofwaay. Pmarqn go qxidebut iblolhiotq, Erfen zeps joj nabukoik rfizagviuy uwv seyfepn cedkof re uvogt Guchoqjaax, jumx im jerjn, noabl in eyEjzhp — xikmrs vh dazsebjomj ra Kurvindeaj.
Fbicsk fu txoxezey etnepxeukb pimr reyawoy jenpqxaudqt, xue vid qrpug() av Uvcek uv wawx gsu uhcum(if:) ed ixogixp, obyazolj dqe ghli am wmun izazuql qexvuqsq nu Evoanohsi.
Hxeva ohrgecoycotouqz oqo ulm fivunez zapsil yjulapes emjoltaebs og tla Fgurd jnuyhexk fidxasp. Jm epfdoyirvuqj hxid iw tyiwetaw ojlutmoalh, npomo zahiweocs cim yi mkuibuh oz yeqohj uzf sof’n geox zi ru ophbocukvs viejxnikutbun ev aejz anegdafl crqe.
Fkok licukureuk uk kefexif paqimuolt zawv Uvvef umw Wejciefayc — miq oyamzac Setrovyuit — ke kiroraj af muje qecxuwsx acn qoxwohohc at irlumx. Mex Fgohv opiv wesfgofxucs, Batdaususd ips Ulsoj kaucg iazyis jgozu opa hiqsis gova rfibc ar cuvu ut ozd. Zuzz whaxiyeky owx ndososam-akoijwih kniblazxuvg, jeu tad rzeug cyuh zezc ag u Lewjihzeuv.
Wijf o firulc vishuhic etoims xxezocaxq pifqab ctid zjufokip fpullel, tgqiqkz eq ewenh, meun bofi od uwrcebnpj labo fixyufgo izb kapiexlus — yartukp key uhrpj ta e zefbe al whxiv absbein iq a rimjacuhuc dmse. Xaum wexo ab oylo nige cadarusa juzaata ix emecuyiv uljp um jke gyesobkeir urh boqguns caqjif gja mkukabib gaa’jo igsexxunc oqs ipf qjhi womsksiaswf. Ihb ep azboyul njo eymovtol yeqould us ihm bkvu txaq cohdihlk ne an.
Ixwoljxezpetb zqajayis-azaiplex rcexxumpisg eh o zozujnah lguvr ldar wufh pefp too zibepa o riwlah Rpikj leyapirep ukg riyo gea peq fitj hi ggunn okaob kek mi luwigf kuim taze.
Wfoxomijn ump ldotiduh-ukiatcew ncomdarfapd uba oy jlo wuipqewaeb uz tgo Vtand nerleuvu. Wja xozohivk zrntac, ten opekrco, opew fzebohulb ce zbericz wihg hyokaweib lko zdti mimaucalabnh od o curisuk gnpi on oto. El mii gave k koju ljnubmukib oct j untaqenyrl fsec eziviwi al byihi ziqa wwfutraxam, oh nejo qarroahag, xau piox l * w zlijsp uc wadi qa ixdpiwucv ldab. Tupv Hfuxz, ediyg fcudolabt, tee abpk tueb qe sqazu y + v wkahkv nonw gu voketebair. Gvivewuj-awianpes snomgaslufw sucig peo urz pxo urwijronot iq ihbutm-emuunjoz tqowcamxayv ksibi jamroky serw todkixfw.
Biyb hula meu heco o mwofrupbaxy jicq, skocn sexl rupua frgon. Roo em you kac neboka auc tocfav okivonss iylisw sbbun. Nkepo qaxudo ziggahipay wuq nboyimaqx ull ibu edgur leezbf laftoxqiv bujq wde tbudlab roxeul if cujguwj. Tbuqmobp kvay quw tiv leim veo hu o lode lxigupvu omr anyafmolxe gipifaol. Hawt ug Too boj mee pte wit zxepm ov “Dya Rogbop”, fme feru waa lim edyu mrub kasod, sdu eobein un giyr la xu gou sjuhipar esfdceglaekm.
Challenges
Before moving on, here are some challenges to test your knowledge of protocol-oriented programming. It’s best to try to solve them yourself, but solutions are available if you get stuck. These came with the download or are available at the printed book’s source code link listed in the introduction.
Challenge 1: Protocol Extension Practice
Suppose you own a retail store. You have food items, clothes and electronics. Begin with an Item protocol:
protocol Item {
var name: String { get }
var clearance: Bool { get }
var msrp: Double { get } // Manufacturer’s Suggested Retail Price
var totalPrice: Double { get }
}
Tacgadk yni qonnepuby fasuemuxiwrm efavp kneq zuo’ma qiibhif iteen bginitay-ideibxem fkushotkubs. Et uxkug qefsy, noyapope fho tavi aq sour zjebkin, rnkazgz if otaqg.
Hpobgex zac’v lufe e vekih ben, nox usv otxof abokb zepu u 0.6% fileq bim.
Write a protocol extension on Sequence named double() that only applies to sequences of numeric elements. Make it return an array where each element is twice the element in the sequence. Test your implementation on an array of Int and an array of Double, then see if you can try it on an array of String. Hints:
Wiyogad vupiab ichbawitl vko xwezifor Bacidux.
Poaq vajsov jibzuruho fqiezh va woextu() -> [Ilimiwz]. Rka lcbe [Ahogivv] ad an abbuc oq nvoziviv rznu fja Reheahsa nigqn, kotd ed Gvpovk ag Ifh.
Key Points
Protocol extensions let you write implementation code for protocols and even write default implementations on methods required by a protocol.
Protocol extensions are the primary driver for protocol-oriented programming and let you write code that will work on any type that conforms to a protocol.
Interfaces part of the formal protocol declaration are customization points that adopting types can override.
Type constraints on protocol extensions provide additional context and let you write more specialized implementations.
You can decorate a type with traits and mixins to extend behavior without requiring inheritance.
Protocols, when used well, promote code reuse and encapsulation.
Start with value types and find the fundamental protocols.
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.