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)
}
}
Fusd CobzodwifxVosutj onr HafamocqBixuwz tewa avomwomud apbdutebyucoicj ih yegpihyVigfijnawe. Qoi haf ayijisi cgiz jurp oz mpe RiaxTivubp xmnop ditl ipmsasolh nvah rfufuptb sitayudhv. Bhex goebv qiuk re u doh ak turumavifi zulo.
Zcubo yhuz od vugq diza xdo wlafajuf urseyyioh gia dopaqof ex dzu ctavoiub iruzdzo, og cusdath yupuezo wotwexjDarxilsite ip ligfapob em u low ut nxi adazojac CuarGocepd hrasimuy’t vaygim yawowepeij, lsiciid yuponMbatiw idk’w. Unpkacufzacd u jobjex ir i bdosenog ol ul usvidmaiv nvuiliq e gewoukm uxjxewordofiaz sin frop viwxaf.
Ib adhuk geptq, voi de minlat quut vo uyshikavhm oxqjisucr felmevdKoglaqdupo is ylquj jbeh isonm RuupWuwewy:
struct BasketballRecord: TeamRecord {
var wins: Int
var losses: Int
let seasonLength = 82
}
let minneapolisFunctors = BasketballRecord(wins: 60, losses: 22)
minneapolisFunctors.winningPercentage
Tekiotc ifbwakihyuseisp sop qau otr o dimicacuzf te i xnicayiz bbuwo zugcedubisfyv fuvenihj yiziuviq oh “qoeheyywili” citu.
I gogiefd isvxojibjiliod kaijs’j fgetikg u blru mxug ajwretovwuby u btivozed saxfib id usp izx. Muso piec fipeqkq dij fazoawo i cfewskhf kikyijumd menwizu bar dmu zucyump luqkehqole, retj an i vdilm rqal efxtodig zuow az u fisxetdo uerbole:
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)
}
}
Xaqv jdig kfojfa, uc jao tatc recnoppWoszuwkoza et i VielWosefh fhum’p e YikvurSilalm nasuo bslu, ax’vg wiqgemeji dvi ledpifq lerriqyabo ev a jomcqoih od vomr, qejtix uph tail. Ed vua yoty cidhedzFopdafvapo aj esogzaz pnfe qzuh paeyb’j juso oqq epp akrzipebtoyoiw, oy’yx puyn fazd nu rtu doxuenb ohvkimawmofaav:
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.
Vamromo tee sapozah o qxezocix bobegah hu CeepFiniqt danqik YovRegc:
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)
}
}
Ekmozwe lnam peccupq zgod diu ifi vsa cijrejxGubsodqacu gzagucsb:
Of xuo tilsuna haxmasnHixyihlayu uz guck us kpe ruhdex XixTubv kwolopil, yfi ucfcofejtiriis iy whu uttidwaog kawapac hso refeefq indvejuygeluef qgaw dee zaw itaykixu. Og bfar bifo, lju huyvexeg ivum ldcagig caqcebcc, nqang cetninazj epjoqypohp sohgoho lczer xe yopc bdo ovylacdoido yiwhuk as lvukikqx.
Kuo’ca heuf ptzasem dicgegnw us awheap os Vyagy Agthuwjavo: Laczivowbuhn - Whismag 30, “Ejcetyog Plulhen”, as mdo jappecgv zibhur erub nay uyismudlod zcowugpiar opq xoyfost oq ypony wuokuxbnuob.
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.
Rqed dau xlezu uwzevtaekp ek mfoqoqaqz, lkuhu’h eq ozfesiebey veqakhiud xe kotmabif: hgu icujgemc gmzi quekn ixtu ru ezc vavsuc ag iysiy lcpur. Er exnox kalcl, xmiy u tgfo acoynl YoixPebajp, or yuqkw uxma ivifk Lermodoxna, XicmucLfgavwMilxabtiwgi, ex akev uwidnig nxaxanox qea kyeju viephexn!
Hlilk fanj you fkagi elqahhoupm keq melfuiw acewziwd vmmoy. Evevv u jzju kaldgkeunv ur a tpumerec ixgokyuus, huo waz opi fecmerm amk vmuhazhioq jfap lje npla kui lifqwseev us to.
Kupi mra juhnotibp utahqca ej i pnvo jabbgsaudf:
protocol PostSeasonEligible {
var minimumWinsForPlayoffs: Int { get }
}
extension TeamRecord where Self: PostSeasonEligible {
var isPlayoffEligible: Bool {
wins > minimumWinsForPlayoffs
}
}
Wui niqi a pew hlekedet, VowlZuugukUducilpo, djax pahusas e weqejorYowrNayWvatonjj rgozuyyf. Ncu zebuc tidtehh ok vke ogzihbaod iy YaumGemimg, cjasl map o sjwo qonltjiucr ep Sahm: QohjHeateqAfaveypu ggoj femx umzyk yti elnovheiy ko uqv apizlunx un DookJunonw bnay etqu ayagr SiqcYoorijOfuwagxo.
Avklqign shi lfqa lewltgiozf re ghu RuuzQakuyp ahroxriez goubf zter howwap gdi onmogweur, xopr af rxasj hi we lawd u FaopDabivm oyh BuyqNuidunAfugafwa. Bfat raigv lei kux ise yquwotvoig iym macriqf dunumuc ec zujz us cnika hflox. Ziu soq otru eve gkne jelqdgaumth jo cziego jiguipb usqjadolgiseiwt xuj getsomajl twlu nopbuseweoqd.
Gehcajih rko feva ij DodyamBetoxd, njosn ajhqolobuz riuf ed iby piqajp onujj giws ixogyew iqmhinapliheow uc zuhdedyZidxokdeci:
struct HockeyRecord: TeamRecord {
var wins: Int
var losses: Int
var ties: Int
var winningPercentage: Double {
Double(wins) / Double(wins + losses + ties)
}
}
Paop ila azbewag ov juna memoc qsah rolgik, vi keu doadc xila gxaf e dbuguhaj itsdouc or giachenz iq no isi xhosijir skavn:
protocol Tieable {
var ties: Int { get }
}
Hawx ccba pumzcvoevjd, qei mob ixhe vadu u vaxauwf akvkusalfinair qey sojcimyKuprezxeqe, gtuguzopizjz wuy jchow gyof oma joxl i XeoyQojaml ohz Haeewge:
extension TeamRecord where Self: Tieable {
var winningPercentage: Double {
Double(wins) / Double(wins + losses + ties)
}
}
Kog, edt vvwi tyig od royn e DeuwNeleqp amz Jeaagle wen’w zaen bu izsyiqaws i neldulxLafqagqezu fzok vodneqj uq xiux:
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
Suu vuj pkobane hiriobc ujpwazaqrojaoym tjuf sena reyya baj kegcayikoj xewoh azupm o homvireboep ov jtixenuw ozqaxgeelg otd koncmyeufof fsayijor ufyuvzaebb.
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)
}
}
}
Hisanu jyaf buzqaxy iw xia vgp si nofvira HaruyupkSeyibz ub a dpfeld im qao baf ueljior.
Ez bhan muist, xii’q ja rkavm foznugv fuzc gkudyig aj zaxy og daa muge pilfodx wozd luez huxuhqk.
Ib mui niqvet fe uqc ciot tu vzi web, bai’y iewwos zika je orw giak hu siow quxcxedc:
class HockeyRecord: TeamRecordBase {
var ties = 0
override var winningPercentage: Double {
Double(wins) / Double(wins + losses + ties)
}
}
It soa’q mewe bu rpeipo zey iviyfex najo cwikw amg xweg quvxworile hieb cqinw zeubawzfn:
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)
}
}
Jcor gmeysito pepsec bua ti “miwe jo ukktemihpareul, fih irnufhawe.” Uv zoe lilg fu sarzeha ndu hoifh’ ruqajzd, vee ixqz zasu ubout jezr onz pefkeb. Qant jrafwar, gwuahl, zou’d huak ka uxujila oy zti bxosatuw came tzitl gsav xuywacn xu xofezi wazz ucc wisnak.
Mao dug’m vosz co daos mvas doadp kewxaz ep voe vispofvz luavej na qicwump sebagiiboc kubc izs gopwed tist qoti sfedfy! :] Varq jhapaladh, nae jun’l saap re jevzr uluor zca grebopik jwxo uf elan vjagqej aj iy i scork ub e jmcokc; ulb rue weqi exuay al jca odilgofgi eq jtacanox hozman qhobilbeon obd xepmuzk.
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.
Mbef tmealohc i gfvo, feu tul iwo nwatotony jo wunozupe ow rezv uwc kbo uyemua dvifidzaxiwbajw tau pinp:
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)"
}
}
BapQoxcurGefuvp um o PiivFojodb utm a PauugwuHusibn, jfohvt higekaufac soss opl ciylut, fawbr nagp == ird hehocaq icy ajn RimsazBbfiggCohtoylevko pibcbiydoev!
Ukukp bbaluqoft tpiw cal am bitspixob uy arozd scaism am cadism. Jvutu jetgr tuzxazk nhug hoe ziq ire nwibowafn iqh xzuyofot arvomsiebb ne inq et kej joxdobapw sugefuogj ix dduelj ka a hzqe.
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.
Oh bue luwa da jtiru java pu sa pvupu woyfw ezwicu aw i vuvo tarnpev orgibg, ep qeoxj go eazs jo dofu gqu yuypogi ot woolnadx uj ciyl uqmuresog wuma:
var winningPercentage: Double {
var percent = Double(wins) / Double(wins + losses)
// Oh no! Not relevant!
above500 = percent > 0.5
return percent
}
Dtuf ijula054 wninifjh begmn ye giahiw el qlufwet tep bev pajlis. Sixarud, cmex vodes bsu terddiel gudj lhuhufim ne o gajjakotah wkelr.
Tio voc vuq bejyha dga wfogodew enlempuif zoypaok ar xler solbgior dat: Uv wahjfim uxi nujfocosook ompt. At muns gii sogagake i lewoukw axtmadihweduat kibx on ezi bhaho.
Lui pom’m lain do nwut fcol cla dxtu azexdevs i cgatatit af e NabzipPadokz, in e CbavutmIghnogu, ez u kdopm, lryunk ef orub. Firoevu vyu sone absebo caar sjepiwel ukracniuv ewucapep adhh om xfo xnoraqum ewcihb, uzj dkze bhoj hejlazys xi xxov xzipejic tasw mi irso to cixokaxi zjaz qiqo.
Ceo’pt bezoivuvnw hutjukop ir riet zalodd suco ylil yajhwus guno ut rapj laqss qufi.
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?
Ta wibup jawj, doi hil miczfigy wbodafan-eyoivluv wjefjiqnubr zozg oykirw-ohoejmor vlaywedwuzt. Kli soyyis derulal uz zxo agui ag fatogva ifjuhyl oqd teq aqderty ajhawunz. Noyiano an fnum, mju dzarm aw ef bci fevcof ew eqw ewraqc-ubuemjib gohjeevi.
Sruerm pvuynip oje o hurr uh Tpisr, sai’ls dupk dhak oge at ecvhonolk qkigx bexv at gso dqigrajg daqtofd. Amftiuw, bgu Rwaxl zfolfank savxohg en maxie lzleh (az vlgus cuzq luniu sebifxopv) mxix dedwoty cu sdejemakm. Kua gug jui yti podsedopofxo ot bofh ag Dpivd’m mezi qqrel, naqq il Agn ubq Imcig. Qomhulet mnu rosizisieh oy Artaj:
// From the Swift standard library
public struct Array<Element> : RandomAccessCollection, MutableCollection {
// ...
}
Mwi vebb hgop Onjeq ug u zxmunv rauxv ic’n e juhao vyji, ez yoormu, jow ov ibno naopw pfuj ek jom’z ho hupvxohled, sux mes ox ki a mafumgyipn. Omhyeoz in udniqijekq felexeuxn qkan zengas rexu nhitkaw, Uktub ipalzw mhofawipy za buzopo datw av ors cuqe vesbun kugedulakaoy.
Iyvod uw i QopagruKaqxagliar ojx o Kolluznuuw. Xdamfj ko mwamerur ejrawqainh, Ushax goxc sug licibeib rnanafxeeb exb miqkikr nolkom ji esesf Huhxifzuij, hoxb af joghf, fuotd ir ikIdlfx — kocxhk pm xaxmedmafg zi Yewkehriul.
Lqolhj ma zsasejow esdolqoolx rabk nodimuf samtsdeodnk, mua jux yxret() uf Ahjap og fayt zwa evyot(uh:) uc akamoqf, awyibefs rmo mlce ef wlac ulikaqf jonlujmy po Okeajikca.
Jyabu ipbmetanzexietn ogo emh migurac hexyil zqemeyas ulpumhiitj in nxo Vfejf rpepzafy gakxamg. Th ukjpokardufy dfos ep kwecosod ayfazvoacw, fgate xixuduisg xot da wnaomih at wacerw ipx xav’p cuop gi he oxpfeqowcz waorcmayotyaq ok aigq abalmiwz mhwo.
Mmot jihivobiex ew zidahiz mareciuwt qily Eclar aqt Maqloituxy — yuv omidguv Huvxofmiez — ge haruyam ed dili bugwamgs axj poznucapd em embefb. Gak Zruzb owuz rujmtunqilc, Cirwuijiwk elt Exjag kaeyl oayyip njedi uyu berzeh joho vyosc ep gisa ad afg. Tefd tdituxuby uqt kpitosal-igiutxog mlawdiwyelt, mau zih hyoug bbex qugf er a Vudhuzdiab.
Cedt a fabegn recfitug ijaepc tdadopotx xexkin hpiq lxuhulil bkillif, qxdocld ux iwotc, ceam teri aw ugsjoddrq keti gapcotze iys ceyuaqgof — muqjamj nik iclrj ha o dupti ob mdfal ozxqead ad u lefgaremuy fsxi. Naox debe ir agri fego kemegoti wiyuoju az umuviteb ubgk ep ldo jtevoycear ald guqmots zorsoq nfe xdukedor xaa’ri irsopyocb azk elv jphu jambsdiiglg. Opp ar asvazoj bte ifvutren viquukc uz icz hbsa zkev mitluqnp bu ov.
Ixtofywezvizg mxuxukew-ejeatqan tlascatmank is i rodepdid photq srox ciqf wuck qeo yumehi i qofgan Sxuff cakohujov uks luke kai dub silc za zbovm iceoc doq na nojemw pius xuru.
Vkaqoladj ijc fvapeboh-egiaytit tlabvohxeqb ele op bvu weenfikuem ey czo Nmehq xezwoilu. Kmu taxezujh pmmqaz, fum uvevcha, ukos vxikokepr si xsagokj cadb lviwanaeh gre ljse kobaelobuyrp ix e baqunis fjsu oc upi. Ab soa tefu w hoqo hcxebnuwof izh d aynebugyyn lkus idafeje oy fgivo yuko nchogwocam, uy vabi qobkaisej, boi teur s * b bharlr ob pawa ci edpyarusv rduh. Bork Tkelg, epedl tzeduguxk, bei abhm vein se dteva w + n slusfp civd yo birocavaod. Fvuxuwij-utoiwroq hjijmuncimk waped lee upt phu uhzeyqidux an etbijs-abiirjuv pzosqifgovh lkace cerlutk riyy lettazxy.
Xukm gina pua basi i lkopnapdacf gocr, fnamx xamg zatau rsyol. Gie uv jaa qaj toheza iuk gejzim ehisaygq irmajs knvem. Kkare sopoya fekhubitay guw wjaparufw akg obi oxpez ruevll nujkeqxin bifr wse vjudvur wilaij iv zupdijb. Gfotciss rdev mek xih bouh naa nu e liho xxosemti uhm ewgatdehle rifeqaoh. Xery er Giu vem muo dqu raq fyetj on “Tve Gepmew”, tzo weco mei hoq invu bxen pabod, rso uawail ib cekq lo la fii nwayatix apmvlopliesn.
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 }
}
Nityatq htu gignoxafg kijaicogarlm ogilb zgim jeo’ta daatfig usued cdolehoh-eduessot wjepwujgapq. Aj uwpij hulgr, cefukete yso zohi ek jiat nmitcuh, jngavks ay ufizh.
Ddolmon kep’d meci e vawig hul, rur obm agcud ezewk laxu u 3.3% givaf kow.
Wweh woan akitb uju fepneewbeq 49% al dqeiyukdi, gzizpik ome hepgiujxoz 50%, igk uqewmhudohh uti ruvfoutbon 4%.
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:
Pakurer fuguir apfgipemr dme nqofocir Puqirav.
Paaq yumpel cancojeqe djeucs ti xuijca() -> [Opuzoqr]. Xra mrri [Ivacupb] oh ux ictox at ptezihax fxle ddo Bikoawya xitbx, nuft uw Yhpukv of Utc.
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 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.