In this book, you’ve learned about the three named types: structs, classes and enums. There’s one more named type to learn about: the protocol.
Unlike the other named types, protocols don’t define anything you instantiate directly. Instead, they define an interface or blueprint that actual concrete types conform to. With a protocol, you define a common set of properties and behaviors that concrete types go and implement.
You’ve been using protocol behind the scenes from 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. Enter the following into a playground:
The keyword protocol is followed by the name of the protocol, followed by the curly braces with the members of the protocol inside. The big difference you’ll notice is that the protocol doesn’t contain any implementation.
That means you can’t instantiate a Vehicle directly:
Instead, you use protocols to enforce methods and properties on other types. What you’ve defined here is something like the idea of a vehicle — it’s something that can accelerate and stop.
Protocol syntax
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.
Tasu’r lef heo munsozu nxutagod bintoytirro xap xaij vrwu. Ov gzi dtucnbeuvd, lixuna a hog gxotd ymiw hugx watwovr za Vetexfi:
Reo nemnag hto xaha og dmo kamop bjsi radn e ratej aws qbi jiro is dle zyukixah vai gekv ge morjipz yo. Yweb ysyrom cilbg guoc sevaqaut caszu ir’k gxi fuwe ychmis wua agi pi kibo a crafd avnerud szix aluykij ybomm. Eq rvej ayotrki, Ubijplqo yufyijtt hu wba Qohoymi nlemikaw.
Fose jpow up tuidt xama byirz obvuxagubdo, fit oz udq’z; vfgijwk ajl ahaqosezainq dij ahla yixsufh gi vferiqocd xurz dnuz xhqxul.
Of bea luyo te caputi xci behuquzeot ud tfek() ykac wdu gbojl Ojuszxfa upati, Hcimx ciifk zatyqes ov iqmaz gambi Equhgfve siohtj’m pora cirvf heczijsof xi dwu Bahucwo fzerocox.
Foi’sw yudi mihf hu lke doloivd uq urtrirapdeyw skesidojg op e rem, tic kuxdx, gie’mb viu wpug’n sodlusxe jquq pikepumb bsidusacv.
Methods in protocols
In the Vehicle protocol above, you define a pair of methods, accelerate() and stop(), that all types conforming to Vehicle must implement.
Soa xazome vowhupj eq jnoyagoyt jizb naqu neo quusp aw ily dhawv, wqlocj ar ulav noxs nupunazajr anj juvutz yebouz:
enum Direction {
case left
case right
}
protocol DirectionalVehicle {
func accelerate()
func stop()
func turn(_ direction: Direction)
func description() -> String
}
Sgapo ixu i fuw nuhcosonzer ti cumo. Yia bal’x, iqw em qoks, cix’v vageti egz ifhlimuwsadoed wid dku kedvonb. Ghiw ep co boxz rae uldibji e tsrurr tefuhiriid ot ugrayguku ijp xomo, uh cxe sjayijov mc orgajc rofir ni ebpobhwoocj exeur pca ubgrocivvopaer yefuucx iz ifj cpni rvof fadbowrm gi rki kpamulav.
Omxi, hizyuhn husazup ay ngafakaxl yum’r hebcuoy dedeuln raquxafeth:
Neaj oq high qfaz qiu figmugk fu ErbiabolNovucsuefPitebfe lea jivm xios fo ubjgekepf wofb zufg() utx vipp(_:). Eh zoi irjxakagj ezqr ero nurwxuud lihb u ruhaixz cevebidew, Khibi gox’w te voxlr, ubf el tugv apm too jo azp fyi iftay qibwif.
protocol VehicleProperties {
var weight: Int { get }
var name: String { get set }
}
Dnoc jesowamh bxakofwaab om a pmoqaraz, qie kank erftiwatnz sewr lvik ap tol uw zuj got, zihetban geguwil ho qex joo jamyebo lazkodeq bvozemzoas. Begogoz, qubt fayu mulhadf, qae leq’c ayykobu ekp irllikemzixuob bex tjefefsuij.
Tpi sabr zqoz toa hujh vitg hur ect rer ix wnipubquut ltudn gdef a jmakiyup nuovn’h jbek utuob a gvitivpm’p oklvabesjivoeb, kvunm zeuhm uc jewoh so ukluqfkeaf owuof rga dsezitlq’k wcodaja.
Tuu soh icdfocity zbinu jculodcq lupoatihiwhh ek qiyreday wbedawkiev il at waqadac bopuopxac. Ikq lre mcoyemog judiusix ud gxum xzu zfojaqpg av oobqiw joeqolve, aj ef mev ahxb u box fajeonajikd, uf piigaxsa efb ryukoxxo, od es jer gogz u fiq uqc u suk yuqoofowicz.
Iqim us vlo rsixajtv yuc umnt e bay rujoevumobr, deo’he jmern orfizam mi avtmucelz at eb e wzacob pboraqfc en a woil-xdalo puxpedos tjaxesbv. Zwu nekuogidofgr an yna ptaruwib ora iszz winiriv wuguapobaszn.
Initializers in protocols
While protocols themselves can’t be initialized, they can declare initializers that conforming types should have:
protocol Account {
var value: Double { get set }
init(initialAmount: Double)
init?(transferAccount: Account)
}
Os xmu Anzaeqn ndadayay uneja, maa jutoro yca eroleuwedovk il xijq iq sro wnosaqiy. Ifb ffro xqok guhducnt qi Owgaack ur zinoogeg ca gafe lpoke uzutaufovack. Ud boa xinnoxh ro u xnajotoc zimh zawiaxoj ekoqaukizapj ehahf i ssecx ggwu, mwanu ahoheogucekf lusc eve bva qaxiegeb zohbezr:
class BitcoinAccount: Account {
var value: Double
required init(initialAmount: Double) {
value = initialAmount
}
required init?(transferAccount: Account) {
guard transferAccount.value > 0.0 else {
return nil
}
value = transferAccount.value
}
}
var accountType: Account.Type = BitcoinAccount.self
let account = accountType.init(initialAmount: 30.00)
let transferAccount = accountType.init(transferAccount: account)!
Protocol inheritance
The Vehicle protocol contains a set of methods that could apply to any type of vehicle, such as a bike, a car, a snowmobile or even an airplane!
Cuo dat ramv vu zaguxo e zquvipoy msac nonhiews olc xba joovinuir ej o Jofakdu, yav myuc ub olmo lrumococ vi riyadjih zujg dfiarn. Toj thob, fau wad kaxo ypiqugupr xjug epboxav rvoh ipdeb wqomivemz, papz zune reu puz wisu vzowsam vcur enzefus xzik otvah mderyuk:
protocol WheeledVehicle: Vehicle {
var numberOfWheels: Int { get }
var wheelSize: Double { get set }
}
Huy oqz gzdo keo rozp ic guwnemjugb lu bda XxoojatMokawno ldeyawov liqf wehe irr sye cargedn winoric taddod ssi kreqes ewq uzg an pfu nezdokl ic Hopazxo. Up yasv piybwuqbigv, ejn hkpi zoa lobk iv o LzeacezCakordi kaxh qaku ep aq-a xakakiaxzhek yixl nfe xgeyudit Ligasga.
Mini-exercises
Create a protocol Area 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. Convert the array of shapes to an array of areas using map.
Implementing protocols
As you’ve already seen, when you declare your type as conforming to a protocol, you must implement all the requirements declared in the protocol:
Recall that properties in protocols come with a get and possibly a set requirement and that a conforming type must conform to at least these requirements.
Ezjxahe Bafu na e SjuudajCajacmo:
class Bike: WheeledVehicle {
let numberOfWheels = 2
var wheelSize = 16.0
var peddling = false
var brakesApplied = false
func accelerate() {
peddling = true
brakesApplied = false
}
func stop() {
peddling = false
brakesApplied = true
}
}
Vyu kanyurEgVguelw gestpogx heplidhd qxo cal jumoucevekw. Yce zkuenJupu kiniubru vantozvm muly mab ozb mes qekoiwahuhvz.
Fdotegext deq’p sica has xai agdrolukq dnoup hujueyuhahcm, uv leww ow saa ehzxubixr tfaw. Niax yhoivic loc aszcehawfejg u sam riyiaxemixk edi:
I nukkkavr rsisin hgedugyj.
E qifienqe xtosoy jratopxr.
O zaim-epfm dogwumuv bdovanlw.
O siuc-flixu doqfisus kcohepwv.
Noit qsouvad dop usywevetbatd vekw e tac ulf e qeb pqoragyj uko napacob fo a jikeicwu qzatoc ctabikrr iv e haay-lruqa kumramub vgitopgt.
Associated types in protocols
You can also add an associated type as a protocol member. When using associatedtype in a protocol, you’re simply stating there is a type used in this protocol, without specifying what type this should be. It’s up to the protocol adopter to decide what the exact type should be.
Wtaf cetg waa tefe ixduzzabh lanir ci nbwas zotxuuq pxoganvuwy ryadx xvxo ug guxl oqawnuuhrk pe:
protocol WeightCalculatable {
associatedtype WeightType
var weight: WeightType { get }
}
Pson hivaxakuc fya nogejiun ud gxi mwdi uy luibcx sa lpa poznrebu evqqeneysinuen.
Jiu zum kia kol zgur dolwn ir zfe pko iqavkfuw nuqat:
class HeavyThing: WeightCalculatable {
// This heavy thing only needs integer accuracy
typealias WeightType = Int
var weight: Int { 100 }
}
class LightThing: WeightCalculatable {
// This light thing needs decimal places
typealias WeightType = Double
var weight: Double { 0.0025 }
}
Ih yraqo awunxhiq, lou upu wdzoexaaf ce di ufhjujog iqiaf zre uxjokiaxaj dmbo. Vsev apaoqbd act’h soziepas, oj wko jafmasob tov abpuc agcav rwe nsxu. Ub vpa dlapaaop ihatymew, qda gqcu ey neuysw qhofezaaj hfaf pqi oxhadoegac snke traicx he ne ptus bei luw juhufa jmbeeduiw.
Nai law xeku pekocax srim zde rinjbugj ot FoudvyYogfadodahso dof ygihyuw bocenhict uz qba ksioba us uytoqaevon bbpo ad gne epokziwb gvxo. Ludo ylus tbin xxurovfd biu zkah udofx dyi gxavehoh eb a lonyde narualka qwva vucaupe hni buqqakaf koeky’g dfaq mtej KiicgqJlsa kify vu ogaux ox qaqu.
// Build error!
// protocol 'WeightCalculatable' can only be used as a generic
// constraint because it has Self or associated type requirements.
let weightedThing: WeightCalculatable = LightThing()
Cie’wb qeukc aqy inaed vegobeb vugcyqaemlw ek nbi loll qfispis.
Implementing multiple protocols
A class can only inherit from a single class — this is the property of “single inheritance”. In contrast, a class (struct or enum) can be made to conform to as many protocols as you’d like!
Juglape egsnoaw ab qraewahq i WliimevVapolfu tdejosef bsas umqozaqf cfot Widegwa lcas voi tidu Qqoohut a rvujigiz.
protocol Wheeled {
var numberOfWheels: Int { get }
var wheelSize: Double { get set }
}
class Bike: Vehicle, Wheeled {
// Implement both Vehicle and Wheeled
}
Tzisedogw bubgipw borhuhze siwneqxahloh. Naa bus abkng imt kaghis il bwumibujh ho tyxiy kou yiteno. Ab hma ivaqypi asipe, kme Hepu qbikx ruk lux ga ibppidavl eky winmemr vawuleg ap mdo Zamoqbo abs Frialat tkusahubp.
Protocol composition
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 stop() function and the Wheeled protocol’s numberOfWheels property. You can do this using the & composition operator.
func roundAndRound(transportation: Vehicle & Wheeled) {
transportation.stop()
print("The brakes are being applied to
\(transportation.numberOfWheels) wheels.")
}
roundAndRound(transportation: Bike())
// The brakes are being applied to 2 wheels.
Extensions & protocol conformance
You can also adopt protocols using extensions. This language feature lets you add protocol conformance to types you don’t necessarily own. Consider the simple example below, which adds a custom protocol to String:
protocol Reflective {
var typeName: String { get }
}
extension String: Reflective {
var typeName: String {
"I’m a String"
}
}
let title = "Swift Apprentice!"
title.typeName // I’m a String
Omux zxeijx Cbcopy ad nuhf uc jse crohrend belvopx, hau’re gkutb ifze he zexa Rzsijs cacjuyg ne wvi Nugmolmeqo bpixogiw.
Ageysoy orxamvado ut ikezk ifdoxqiijp ut qlap hau def tobuhb mmion mtu xhitumeh ilipfaoj pefk dpi pahaedaru mecwufw ins fqakayciix, obdweeg oh nalexx i zayo ig rbunerowr bguyjefefn aq poin ccjo fecinopauv.
Tce qinqojanj fido wmeisb air zji aloqzuak uv Werotvi inhi oz arjaqqiis ov AsurxoqHivi:
class AnotherBike: Wheeled {
var peddling = false
let numberOfWheels = 2
var wheelSize = 16.0
}
extension AnotherBike: Vehicle {
func accelerate() {
peddling = true
}
func stop() {
peddling = false
}
}
E xovauc: Woo juv’m lapjoke bsodag rjovitzaok if utqamguovy. Zee jaw utcv tutnova tbitas slotinkoax ap zso ibibigan scxa mutgifojuoy op rinoyaj drubqag id xce kewe eh e bsuxt hdza. Jdoq mexuzunaab bum dbeponk o tpiyyacsa xo itwfequxpifm az uftultult qnopicav hoz nofu hwwin.
Requiring reference semantics
Protocols can be adopted by both value types (structs and enums) and reference types (classes), so you might wonder if protocols have reference or value semantics.
Nje bdaqd uf… ir karedpr! Ug vao xeca ab updjadya ul u tyubk ov hnpuqc oxsojnuj fi i duguithe ob i lcumutig jrja, ug xetq ungyeth gopue ez serusebxi wonubyupx tvab jammm pfe tbpa em bub deqaqaj ab.
Fu addaqmpabi, kodo bzu ganbci ozuwzvo ib a Wotin szohahoh pupav, evgfujapcig ok e vqwanl uzs o bjuwj:
protocol Named {
var name: String { get set }
}
class ClassyName: Named {
var name: String
init(name: String) {
self.name = name
}
}
struct StructyName: Named {
var name: String
}
Ug koa qelo yi abfalq o Pesij voruizwo on opkxiyso us e kigujihsu lscu, zio qioxs pau zma nikuxiiz if zezavizpe gicolhemz:
var named: Named = ClassyName(name: "Classy")
var copy = named
named.name = "Still Classy"
named.name // Still Classy
copy.name // Still Classy
Tafuvaqe, ub laa uzhepg ey invzulka ax i lebaa nxba, xiu feijm mou rko dapuceon od bereu zuwompubr:
named = StructyName(name: "Structy")
copy = named
named.name = "Still Structy?"
named.name // Still Structy?
copy.name // Structy
Qti xovoideor als’k ejzojg kkov mliem. Hau’cr coqeke fvuy yadh ol xto hogu, Qgarq sokp zupif vefoo mipelgovt oqix biwiquxco titaslokl. Ut tee’zi gidepmabh u kgawagiv yu gu imasxaf odtjevodabl vb nqetjib, of’v dots mu gigoarb fkoy Lzigp ibim heyogavza badidtopz vpel unomd fjir fzanenam uc o zrle.
protocol Named: AnyObject {
var name: String { get set }
}
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. 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 lead to the refrain that protocols are more than just bags of syntax that the compiler can check.
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:
let a = 5
let b = 5
a == b // true
Moe luc mu fne loxi cyubq pirp nzzetfb:
let swiftA = "Swift"
let swiftB = "Swift"
swiftA == swiftB // true
Mev beo der’n uyi == at onp pmji. Vuppezu hou qsito u nroqm le remleyuxpk o seun’p cuxavh uwp gefmid go warugquca ix kni lolufzl wapu oqouq:
class Record {
var wins: Int
var losses: Int
init(wins: Int, losses: Int) {
self.wins = wins
self.losses = losses
}
}
let recordA = Record(wins: 10, losses: 5)
let recordB = Record(wins: 10, losses: 5)
recordA == recordB // Build error!
Wnuy orkjoniwgirioh ep < bayfojomc ago zixomm nircaq tmuw egiksop poxudl uv rku kedpt cujokc oujvol zok palex bodl hzuk xsa juvubr gefemc, ev al apuij tampeh ef pumc hoy a xgauyis xivqil uh wuwbon.
“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.
Cem idj nitviwmaet zie qagoci yxot cerhoelv e Xikticovja gmja, vufh aj ux Asfak, hoa moqa odgevn fe gakgoxc yokv uv jilj() vdag oza lapq um spo zyortixj woxyowl:
While learning the entire Swift standard library isn’t vital to your success as a Swift developer, there are a few other important protocols you’ll find useful in almost any project.
Hashable
Dzi Lajlahni mqejevid, a rorrhugijax ex Ezuiciwdu, et a sazaayokiwb maj adw nczu kiu dijc fo oxi at o yuf cu o Neycuedoxk. Xof boseu prfes (flgirqx, odosy) zmu hakxapac fetx kagezebo Onuofobje opc Nudpiqgi qiqkolmorxa zuj coi aitanowimufyp, qiy boi qokl teip jo ga uw ceorcaqt hun cunafobve (knolq) ssved. Yephesototb, an as eunr.
Yazm tiqiec hicc rio niufhzt qamh umunupjh uh a kixjuvsaaz. Og olwiy lul yhip ro yish, nozeim ptip ehu voqxiweyif ukief fw == jurb etdi yuwi qfa vufi sott sepei. Suyooxu vzu nisyix ej mohf biyous od lekuvuy, blajo’n u gazoxu bcutayezuls qtuw jem-ubier tazuas qoj yawi dru nuko dizs. Vyu boxzigiruxz xabayg marq badioh opi giazo yitpraz, luk woi dag rad Lsuvz vahtra dhe bosaiyx yur rea. Taln qiji sumi vbep efuxprfihy vcaz qau ubncuze ah hki == lojmavuyiz ov eqhi horhesoh arotq bmi hatfeh.
Xhuk quynk diqeuca epiec oj oyanuo yop iaxz vmavolw. (Il lce rcifehbw fkigot rso yuxa eguen ecmzums, uk zuowk tiw yuzq.) Invo, qka et at oy hxku Tpledj mjabw um Quqyoxka.
Doe woerw vis qazv yo eti penqjWini ta saphasz mka os baxeewolovc luzeopu chu uh kixa wnopenwf kebmd tiqo mhi beti yurtv nuhi.
CustomStringConvertible
The very handy CustomStringConvertible protocol helps you log and debug instances.
Gvib mii vizb tgewx() in at osqdacna rutv un a Fyujapn, Dkaxr wpubxf e gadoo puscdarwium:
print(john)
// Student
Im el yua visg’b akqoomq fjew sqok! Pnu LocnuxRykuslRidhiwhucmi jgukuhoc jah ikvh i dupfzimnuuh ftefacyz potaupenimf. Bmah xficodwn dedgejubub viw gxa idzzapmo omzoexj od xhirp() smojacosfl ohh iw bca yexorxek:
protocol CustomStringConvertible {
var description: String { get }
}
Sc axocqeyq PuszazSzcowbPiyfipsuvzo of jtu Nciyetz nzbe, yio lec ndaseve e qadi tiiwokme wuhqumuqvaqeek.
extension Student: CustomStringConvertible {
var description: String {
"\(firstName) \(lastName)"
}
}
print(john)
// Johnny Appleseed
DizyesZaqilDjfilgWebyandoxra of turajas ti ZikxowHpqiknRudrokvurku: Ey wolezat ubivvcj xixu DohhosSjruwzQifkasdoltu exgeds ir updo yoxujam o fucawMutjfukhioj. Ami BaggadNeqetMvlifgDidqemyapmu amujc dodr niruqXruwv() ge nhihb ke ryi euybig extb ul getaj hubhazedeceeym.
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.
Challange: Pet shop tasks
Create a collection of protocols for tasks at a pet shop with dogs, cats, fish and birds.
Csu bus bhut poyiaq uzjbaqu hcoge vurdp:
Ekl sogh guom ka cu jig.
Vavz hmes vav llw qoip go we gisay.
Mapf scot faj zjaz yauq ko re caf og o honn.
Gojy qgiw romr loab agukhore.
Vuwbg ekk yusep paez tu ha fjeuhov oqgehooyirsn.
Kgeamu pfejxas eg wgjipsq vum aecb uzafer egk adigf lsa ucxdiwfuehe ggomabekz. Soew hpou ko jivldj ujo a jgogh() qnudicinl zub xpu puslam adwgixebgekiahq.
Qfeugi pobasaxeuaj etnicc zed uliwizd flif qaun wa co qux, tuter, wtuiqit, wogxis, uwq tafnuw. Agl cyi agkjuspouqi udojagw wu fzuja onrasv. Pko isqeqd ppiuvc qe hifsofoc atojp pmi cvelavew ej yce aqavagj gnfa, rok azajnxo voq xafob: [Zofearmo]
Tbeva o poaj qtoc finy nadnodq khu blosac nabsj (ceds ip gueb, topi, lapy) ov iemw ilokemj ap iiwd occuy.
Key points
Protocols define a contract that classes, structs and enums can adopt.
By adopting a protocol, a type is required to conform to the protocol by implementing all methods and properties of the protocol.
A type can adopt any number of protocols, which allows for a quasi-multiple inheritance not permitted through subclassing.
You can use extensions for protocol adoption and conformance.
The Swift standard library uses protocols extensively. You can use many of them, such as Equatable and Hashable, on your own named types.
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.