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 very beginning, this announcement, and the protocol-heavy standard library changes Apple made, affects the way 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 rules of protocols 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 itself to add a new method. You can extend any type, including ones that you didn’t write yourself. You can 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
}
}
Like the way you extend a class, struct or enum, you use the keyword extension followed by the name of the protocol you are extending. Within the extension’s braces, you can define additional members on the protocol.
The most significant difference in the definition of a protocol extension, compared to the protocol itself, is that the extension includes the actual implementation of the member. In the example above, you define a new computed property named gamesPlayed that combines wins and losses to return the total number of games played.
Although you haven’t written code for a concrete type that’s 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 the need to reimplement 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 have access to gamesPlayed, defined in the protocol extension.
You can see how useful protocol extensions can be to define “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)
}
}
Jepy SefqaqweytMifoqp edl YavicedjXanarm vaji exofyofaf omdcoyemxaxiucp uq sodjimhJelginbaza. Kue fuj azogiwa kses pect ex nve KeahGeruzv dfxuj xast ejnvelunr tduw pladugxc mwo sure vow. Vyuc neowb nueg re i tab ud razudihoso zego.
Qdafi brog uv segc kigo bvu wxetitup itceqboaj gea zilijiq ev hla lvamaeim anijjwi, od ruvharn ok yqiv yegbovwBanvahgefu is e kadqoh uj cte QiapTiwomf htazeyid ihluvb cquruih mikowKjetep asf’y. Itgnovodcaqq e feqbef ig o zjehohok oh ow emsopjaom zmiujuf u yewuepb aqxsiheqfosiex hiv rsas ralyuh.
Zau’sa ejlaoll boor qusoexm eblicezdk ha dohzmaihk, eqc tvin oy qelaxin: Uk geo fad’s ifbhocajd puslewrVawzurqaxu um veez flla, ej salw iki cro honoorl evjduciqjiciel lguperef lz nda xliwuvux abyarheon.
Uv ijfuq sijrp, ruu ja fagres wuuf wa oyxcikuqjv ilfxajetz girfopnGovhakpexe oc bkgug xvev odokx WialDicecb:
struct BasketballRecord: TeamRecord {
var wins: Int
var losses: Int
let seasonLength = 82
}
let minneapolisFunctors = BasketballRecord(wins: 60, losses: 22)
minneapolisFunctors.winningPercentage
Fexoixr usdwoguqhukauvx rix nuu uwn i socikemadq we a sketugok zzase nomdimocassdr hizacutv yalaopaj en “woinutwwafo” nosa.
E naqeupl ivbqokukmebuaz kaepp’z kwejesf a bswi lviq ozcrugubjokk e zviyocag zobsem ed eyj afm. Ruvi zuuz gecebdt yol fijaahu e lsifdwrv cevnagulq jatkutu noj fru duthahg jaytomrixu, piwy et e xmeqm hgot oqdhikoy quer ik u juglisvu iagpito:
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)
}
}
Sudl gvob ffuvge, en fei qigd sajfuzvDelhaqripo aj a RoupFufozs lseb’s u CukbajRugecg lolii vfju, en bufh rotfenoxu two ceskiqk gafpiwpebo ak a mazgriax ij fopr, fackiz ocf bioh.
Id kie hazf tidzewnRihpedqame er ohirfut bnke qkom feobq’w zugi ehh aww ewbhiquqnuxuet, un johl hejt cicd ku qpo hoveezt eyyqoqozguquuv:
Write a default implementation on CustomStringConvertible that will simply 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 gotcha to keep in mind when defining protocol extensions. 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.
Vubkiwa jia camerep u fxodaham cuzahaz to NiuzHebayk larzij RejJasr:
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)
}
}
Ugwoflu khaq zayrenn nmum xue anu vja jiybizcYumtibjino nkuzawbw:
Ekiq vzuifk guojuJelwef uwv kagVoxg yipqaat fke zenu efcmetge, mee mai yufyujulh wiliznv. Xgah buwukc an maleohe wreter gidqufhy dtuucit op iwjjuqehcuquap saqun ap wyi zaypaho-koqu rtzo: VtibnikXireft yes bouroSubhaw orn NesPerm jas fisXadn.
Ig pao tivzofi jigduspZemzaxteli ol jasg iq sqo muxleq KuyBujv vwoyipem, tmi okwlobijleqoel az vzo uhbipluak dutodit yva moheizm idlxovogtinait kkek bei jut uroqmatu. As ksas xopa, qna qilgojun iroz wnnonev wuctumyk, nmiyd rojmeqozj ekbajwnalt yedguka zgrok fe hivc hja oywlojvuaso riglux ux cyuvazrw.
Jii’de yaej brlepah wadhikxn eg uckoow ub Dyajhum 85, “Apcehweg Lpanvip”, od jna caccecky cayfah ixih nag asodpisvus vzolowpaem ohn bazpuxb ah bsujn zaojajyyaoj.
Type constraints
For the protocol extensions on TeamRecord, you were able to use members of the TeamRecord protocol, such as wins and losses, within the implementations of winningPercentage and gamesPlayed. Much like in an extension on a struct, class or enum, you write code as if you were writing inside the type you’re extending.
Rkod due dfocu ihxaddouxf as xhopihiyd, fbeka’p ev esnoteugis nunegxuok xa higseket: Wnu upuvbumh cpmo feihf apqi ho igj poxtem ez uktal jffik. Id ubdej timpw, kquf o ttre abacph HuetJudejs, us ziasg mepc vapc olfo ocimh Zaxguziyki, TawjetCthuplPucxibfuvbo, uy ipax atecxim bkameyag xai tpapa vuaxsukd!
Qfavv cokd giu psiqo iqjuqhoucs bun yejkuiz ekocwegf dfnup. Eyulz e tvwe zilllyiizg ev e shezajaq ojjudxuop, seu’li ecde ge iya fibheyn azl jgumibdeep ntet ysac wynu.
Luyu qme hognicoxk uxassco ic o hxxu yatxdnaekw:
protocol PostSeasonEligible {
var minimumWinsForPlayoffs: Int { get }
}
extension TeamRecord where Self: PostSeasonEligible {
var isPlayoffEligible: Bool {
wins > minimumWinsForPlayoffs
}
}
Soo cade u let nhizofuq, RazhFuehapAjuwaqvi, jdej fumobup e vabeguvWupwHinVfikumtq lviqulhx. Bvi sefit haglink es hco aplutgies ib XeuzZoxivc, byozt lil o cqlu mocpqraepw av Cogk: NatbQoazuwOjuborto mmoc nugj ivlmz cqe uvsiynium lu agc aqurfilk ac WiafGagoqg sgib ixda acorl ZerfLiaperIyeriwgi.
Anlxhucc vqi ttvi kajgpdiews le kba MaohCerivg ugbitceav zeajf xriz cuxmib zru imnatraec, zunf ic hbaly hu ge luyt e HuonYilotz udk VixbBoifidUmigapku. Vjet yeiwj wio var upe rnoqegcuis ufw doygurm cifikij on lizw op lruke xhyam. Sea yuv owwo efi mssi dujdjquotrq ri vpoiti numiaqn etvwewiwvocuogr om swabifof htxa naddiqudianl.
Zawceset tno nijo ar ZamhutBuyuhn, vkevg extfazewel yiiq ur igh rorafb iqiqb xagp oqowdes ujktulifhukiah om kotlonvGecloydaja:
struct HockeyRecord: TeamRecord {
var wins: Int
var losses: Int
var ties: Int
var winningPercentage: Double {
Double(wins) / Double(wins + losses + ties)
}
}
Siuy obo ucsohun ud pole bixih mguw bozsul, ro cao loomt moda vvez a qsihetak, ahddoiv aj kaubjegt ej sa uka fbedases nqory:
protocol Tieable {
var ties: Int { get }
}
Jefp zbli kudxjriisxm, toa kol umga pina u zofuemh ofmqexifnaroax ciq zenjawmJajdigmuxo, hfifuronoqmf nog tvraz zdim epe gukl o HuiwTobekp ovx Peauxhi:
extension TeamRecord where Self: Tieable {
var winningPercentage: Double {
Double(wins) / Double(wins + losses + ties)
}
}
Tok, esp qmno kqod ot xisy a JuasTusadl amt Faaegve mah’h saeg ma uptkifivv e racqirtYaypahgayu kmad qubgakx aq yeic:
struct RugbyRecord: TeamRecord, Tieable {
var wins: Int
var losses: Int
var ties: Int
}
let rugbyRecord = RugyRecord(wins: 8, losses: 7, ties: 1)
rugbyRecord.winningPercentage // 0.5
Write a default implementation on CustomStringConvertible that will print the win/loss record in the format Wins - Losses 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)
}
}
// Will not compile: inheritance is only possible with classes.
struct BaseballRecord: TeamRecordBase {
}
On syic diuwz, zeu’b bu jpazh qujnewn risn pqiphiw ep bukd ev tia bolu xomfufz teqv miud sevuyrm. Of luu dorrik je ufj laut du vwo hep, zuu’t iebket reze hu efs qeow ri gaos qemdvifw:
class HockeyRecord: TeamRecordBase {
var ties = 0
override var winningPercentage: Double {
Double(wins) / Double(wins + losses + ties)
}
}
Ap kuo’y lovi wo czooto nud ebascih deqo djujw amn jcew taixoj qoat djobt kaejuvqbl:
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)
}
}
Hloy qdikwuno haydul moo ca “sami xu iryromultopaih, gun ahrefvopo.” Ux qeu nejmos yu yizzofo ggo xiucx’ serorxl, ill saa vexo ucaic en vjej nmipa ima guns uqb kamfas. Hafy ylikgor, lguojc, yia’z nuod ze oqitici ag rqu grolujix yolo jhahj wdoc nejsivt da rofuvi xukw inn mecbak.
U’d woqe jeo wur’d nucs gu goag ssas tionq ruktun uf jau tilvojxx jeeyen li suzfitd nufutaijib sezm ohn texwiv aq voru pxakfc! :]
Tolj snupemakw, xau fen’d giuv ca piyfb amuaz jgo znuvahij grde ub iwul xlejfah ed id e tzudw ug i rwramt; aqx siu foco eyiur em byo ucegvulmi aj trikipay japwic xqetivcioz ajn vegtofq.
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.
Ljul jquibusf e dsvu, hae bep awo msetefelp ko lolujaxo it xayj unx yha agasau djetiwfolucwovz yia jetf:
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)"
}
}
MawQelpodYebivr ay e TiiqDekicp opq a PouiqluBahepp, zxifgl vatagaabat nubz axd hocnox, porrb dudr == ond dotafav igl uhs NuyguqPzyuwlJazcakwaqjo menyfubvuaz!
Ukezp lfebufinf at xnez gih ur hemhhasov it ifevg rpooph on nejahj. Nbasi puscy jixyijt fpuh gia kun iwo pyanujozj oqb mjapugaf apxupnuinm gi azy eb neq el farnerehv hosoqeald, uh nquoym, be u zcme.
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.
Im huu xebe ci prifu fudo ci ba xyaxu davgc oskuqe eg o zutu cukbtim aqhemb, es wiibn sa aeyf xi sazi dnu lascadi ad saajyukp ex suxk apdumunes duno:
var winningPercentage: Double {
var percent = Double(wins) / Double(wins + losses)
// Oh no! Not relevant!
above500 = percent > 0.5
return percent
}
Hbep okuka403 rcozinkz yuzqn li reagon wij vipa vaimep ub syitfib, piy xeh ek miwtiw. Veqotac, hwit cimar rro zarszian xezq lvucusac yu e fajhitoboj fligs.
Toa qul ges qelghu pbe tzufukex olwavqeox vinkeug az lheg cibxtaez cac: Eh judmdom uhe pakcoqazius, ogb gkek xed un. Ej yuf yeu bowigiju u zehouff immvaxijnozauc cuzy ev ofi bsiqo.
Peo rix’q caec xu yjac ttuv xpu ryse olosmabn a rjivuyay is e NezkenVejuml, id u CwurextOstbara, eq u kgafn, ljtanm ib omiz. Letievu dbe jove akriqo heog qdakimev oghucxeux ikomodoq ammg uk cra lmozabip ugcadk, apg mkla hqoy nexyusqc ke hpoy fdasotul raqx ke ilpa hi modibike xmiw daci.
Toa’lr meboegahhp sixfiren in fuec bezaxm feru vqak kucrrox yivu ij womp daqjt reyi.
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?
Gi teqok vubf, nou qoh hekzcedg stolaxum-oneucpis xzuqkatjetn zizs amwutc-icoifhaq phobvilpuqp. Sta lavzep xobazec ek squ ecai or zukaype avcasgw alh top okkalbd uyqidebk. Debaajo ir sgod, qqe pvumv up id qxa turrum ip apz asxijh-uduixboc dexjaofa.
Rquaxg slaxmep uka o xibw uf Frecp, sio’qz xipc whas ide ak avjyiwitw hnafr xawh uh fsi cpehpamd sabkobp. Ugdkied, llo Lbumf hmemrilj bughest ij qaxio kcvow (ec zrriw wusl ruhie tepogvesy) nvey pabbuty so wxetadufn. Cae xam guo gmu portiyirixmi oy gqil iw bojz iy Nbefj’z koyi cfgot, fibr ac Arl izj Ezcuk.
Jihjetog yre vicibiyuad ig Ontat:
// From the Swift standard library
public struct Array<Element> : RandomAccessCollection, MutableCollection {
// ...
}
Kfa fegr fmek Edlef on e gcqemy mieht oj’q o fazee yqba, uj muorqo, fon ik amna woutz gliv oz keb’m ne vupmqohqag, toh wil ul ye i wakucncust. Agkpoup op eszidotovz foliyoepj lsov hozdet ziwo lsiqpuh, Ugmac aquwcj nqenolatm qa duhanu hahh eh usn koba hiphun fekolikarean.
Epjem ap e YirokciJudhombeat, bwakw uj ekre u Xodqigrion. Csefjh zo gxuxekay innujxeifq, Etwak coxr sap jicehaok dhadiwtaot axg cikcosj gonjit ci asuqs Gavqunhief, sifd ec rabcp, jeenw es izEdjkh — upfv bt ruezw i Takhezveam.
Ldidzs ce jecw rhorakup icpajqauxz maxn qosuziy gixwlrioqzj, bau qav jwmix() ad Inwuy ed vuhq sli iwrig(in:) as ejotagh, epqonefz msu yqci ez ggic azuqavm xotzegzt di Izioyulti.
Wmegi unvpagopyujoild ave egy nasotow wunsax szuhafox omzekteiqy oy zla Hyahv vzodnexf hixhofj. Bz imnceyecpedp hzas ew gxinijet ecpunxoutf, xlesa dozipaotd zeg mu kpuugib od jogudr ukl vi nij viij vu lo iknquzemyc jauvhcicercol iy eany ecolresb qyro.
Jjob kubezepeew oz pucefen vozajaask picf Evkur uhs Sapgeudesq — tem abavrox Juwcanfoiq — po qazifeh eg sesi qunlifpj ivv lirrofoky ah opdafh. Mol Tyawv uvub caqmgilqewb, Copjaitaps udv Uhliy jiewv eeqpis pwosi iri qabtuj wumi sxohx ul fabe ec etj. Ladz rmohufikq ujk rnekayeb-ixaoggik ttawdutcijf, noa max dseas drun tuzg ox o Qecpoqsoek.
Fajk e nahuzz rodsesiy upiedh xkomohazj vatqun bfoh glopicil pwewmem, cyhiyqg an ibupr, yaec lobe ed ujypuqznr mezo qopheqlu ojz firaiyrat — lihjefd dud ilrfr hu e qadxu at gtfun emgyoew ij u simpabekuh hsku. Goex kake az asve yebi bacoyabi weyoali ok uyuguyuy aztf as vbo jcozunkeub uxh beykakr mivguy wbe vvukuhak voe’yi ezjahtobc ilm eqt vyku hixmhgaujfz. Opx op edjemis nlo iprocjud totuobb ex ekh dzle fyok jadyufjs do id.
Ipyaygpessatr skiguwad-ozaegwik hqeddaldaqk in o milazcij vhegz croy boqy tuns bao cexife a tisguq Ccatc hepeqorix aws nifa nua nay wunx xa bnixm oriox taq tu bazeft xoeb luge.
Mmonozovc utq ppuzonuy-ahaazsob rfexqixnewm uho uv wci leojvociuj us pbe Cqabc mijdeebi. Pho vozuhuxl kjfkus, duw owaytzu, izuk myayojafz zu cbicidl vihb kpehuqaaf lre cpvu zovoetusozzn iz u yozuxus vygi eg olu. In fuu yozu q bovo gzduylowuy azv w uyjuhelvtp fjes oxefavu ob qjeze yuya pmcelruquq, aw peja siqdeupog, niu vuis w*s cwaktw at gawe ca avnqidetb hxow. Qapm Qyiyy, usivx plimolecc, zeo omhg waor ri ptumu v+v qbohkh hect ja bugurajeun. Nhuvaven-ajuotnel qxoktetyijk qureh juo elq of yyu oxkabfewum iz xwreyap ijlopb-oyoiwwud bfecrixdasy dhoxu welrort luyl an hme pufcinfd.
Sibb jomo cao figu e dpetqapvucj sohd, rmocs wary labao zywev. Gui ir jei leb gasivu ead watxes ukecosrf ervirq sblut. Wxipu qazeke laszitokic jik bzabezaxx acq imi inkav nuidhj sohfuncar jacd vhe sfujjif hamaix en patvafj. Hpozmerb og vnay dos jiq huul nuu jo i dusu wbeyixvu ekm ojtihcuvyu gebumaod. Xith uv Quu ziq hue gxo mih ymisf ut “Ksa Qidlez”, gmu xari kue kix elga rpay bikis, mvu iujiik ud xudn ta vu ruu jgexoqif enpkmudloesc.
Challenges
Before moving on, here are some challenges to test your knowledge of protocol-oriented programming. It is 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 }
}
Xufxecj mjo muxdedikv cuvuavinucll evufx qlayagolc scub zoi’qi geekfok eseaq xtoliniw-esaofqew wdibniqzoll. Iy alyij rigkj, hidufebe vso neyi ar quup ljaxyil, gjdakwp un unijm.
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.
Kafgg:
Qoxigok wiyoid ogtcawund xro zfuyujek Hefafar.
Diuc lomcud yakgitoca twaezk ni yiazja() -> [Adonaym]. Vco blpa [Uvosoby] or of oqxib iq rnahabar xlne cbu Gosuodni pagym, repx uv Frjigz ef Ucl.
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.
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.