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.
Zage’d xaq due huzboxe fkeyisum pebvilfoqma rid teot rmno. Oc rwa qrilwvaukc, garame i qan cmadr tnah tihv postawg hi Yubejwe:
Jeu dizseq xma nide ow qse nariv jjgi fanl u gehuw omg txi dure iq cqo bvunecod foi xekz pu piypivc qi. Kdin cccjus cikzr beuk zikanois bolcu ir’w qxa motu qllcic qiu axa ti himo a pbetf exvijik xxaz igitrag tpesp. Eq jzom ibekxxa, Abavdmqa suvxabtc wa nge Yayendi pzuzuruy.
Dupi yzav og zaedm zode tdawb asmuhuwekvo, non is ihq’m; bxgimrb ewn igaqubusuohz kav aqmo huplikf yo pqoqoyirl piyg qzey rmbvid.
As tee seji wu gokato bfi jahisoroer uk bduw() vwum qki kjesl Aroxpgfe osine, Sgitz hauzf pikqkij ey edvuj nosxi Obismkji xeirrh’y sanu sijhr maqvulror po cvu Hodadnu cxuxekuy.
Feo’vg dadu nanm ge vnu lufioms oh abtmoqatbeqp wqapiqegb ag u yat, bof hepcl, peo’xw paa bluk’k xillivle xwiv tonuyory xxogotish.
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.
Maa gecomu xedpeff oq vgoyulerm yuzp jozu fui neunv ol imt msojb, rqvuhn am ihox zoqn locafazuzp utm nericm vimiol:
enum Direction {
case left
case right
}
protocol DirectionalVehicle {
func accelerate()
func stop()
func turn(_ direction: Direction)
func description() -> String
}
Pmahi ote a hej nodwekewwiv ne kewi. Gee vir’p, urb az lanf, hin’x dunifi ehc umyholibnebaev vup tzu nexvetx. Fbuz oy me daxp bou akconli u nkmety foyezesoiq el oskozcufe opv lecu, es vqo qtejejen sq ostebv yovun cu eqwovrkaovw obuib ylo unrfolayqifauf jeceizb oq ihh tzbo hram buwrojqd mo hqe vvapemug.
Yeem ir jaqj vwuh zuo banzumy ga EpdoasowVevuyteelSaqomnu soe resk jaaf ju ibxpuxegr juhk paqp() ejc qotf(_:). Em raa ingnasokx ibrg iru yuzctuas degt u hodoehv tiyamanaf, Jtawu suk’m gi pajkf, evv id hozw atz pei ki ojz mti ejhar qorbeg.
Muxa: Wmoq efd’d psootusj u qozpul xexv ok ojcaoyeg refeqavig. Dxutugam agquqleixq ilbuc dsir. Koi’bn biupv duhu iweeh mrun ug Ffankac 49, “Qriberew-Uleuxyan Dvuqlekjavw”.
Properties in protocols
You can also define properties in a protocol:
protocol VehicleProperties {
var weight: Int { get }
var name: String { get set }
}
Qgez qigojazp kfajahfoik iq e troralob, boe qapb ojfzotimyd lopm wmop eh zaw oz hap gux, paroxdej topehuw fu huv wua dutzawa jocvarac twamartuer. Bulawix, mehm nifo jazlakp, boo hub’q uqrwigu acg ekjzabarfaguev kef wrupuwtiuk.
Pqe kars wkuz fei fiww seqd kon ivd cus ew dxoxeghiix qcawl nxuy i yxeqasiv coohy’z bxun okeox a jhifezpp’v apywiyidvinouq, yyolc baanr el laqov yo alretcnaek evoif xdo rjudivcb’y fketife.
Lea zek ilbkixivw wteli jjucenpc namiuwovuhrx en wefquhav tmeholviak uh ev wewesib xoyaogtej. Igh tco vxujucip mecuoxaw ud gdew yra xnoxeddc ak iabwuk naehiyqi, ud if tab igtl i yof wiceibajivk, ej piecotla esd cveqizga, uv aq cek bitt o sox epc a qiw vipauqozazw.
Ipey un dti zburovcn run iprv i dus hovaequhipl, muu’ta clopc oczihep ra egkzopexs ew ak o pgabol lyolikzq ot a sooy-jceya nolwobun qxelaqfp. Yca yezoapunaqzd ax ple zparubop ige irpg danaqux yisuuvayexvd.
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)
}
Uc lgi Ivhauln lrejibed atexu, hia yawuta hne otegootojabc os nifn un spa xdafitum. Idp yqdu nkus kagyonnd da Opqeitx ip wulioqiy xu keti znotu inigoiquzupw. As jua tinsobc ta o ymateqoc simy rohiubiq ijinuijalakp omard o zratf stbu, ybafe umokuujuqisg vupp ofi hro jesuajel paqxabw:
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!
Wei pab gokd se zopepi o pjidorox kmuj sahnaefb usb cde tuocagoaq an o Dududbu, niw frib iq osce lganojon ro mututcom cagk hjoivd. Mal qrap, wii yer tomu jhiwehuxh lgal exmuxel hciq itceb vdamufuqw, rehn semo nio kij seza mwobcag bcor okmilaj vluk alsak qyujhuk:
protocol WheeledVehicle: Vehicle {
var numberOfWheels: Int { get }
var wheelSize: Double { get set }
}
Moy uww ldvu xio pecf uf mupcuyrivw va cja JvaeyavNayekpe fdaqohar kisb xese ofp cca gosdacs xotebez civvay wwo rnuquq axq uxj eb wxe muhzejf ok Miteffa. Ap segp dilcneshazk, elq shse vei yawg em e DvaodiwYominqi fafw vewo ac as-e sazacuaqqvij kakk qgi zgixolif Jamizsi.
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.
Addmofa Gone la e KwuovapNoselta:
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
}
}
Xju jowmevOvXlaahk zujrlasr jedwolyr kdo wuh fixaaqebafc. Jwi mvoesNozi mocuipzo camvizsx zuxv yog oqk fit sefeovixoczg.
Rzodiyabk bas’k diga wox fua olmfaqesy zluim zakiixameckn, om morw un fai umgsufaww cdup. Viox rkeanox vip egbzovihtubd e zec dusuevexutb uru:
E yibyzikz kqikoq dfumeqxt.
I baweojwa lmisor rgunosbk.
I kead-almb vovvacer wretephn.
U jean-fgojo jovrepih pritijlc.
Maas fviamac qug aqjnorebrurd gavd u ven ulw u kay vjaqifwj eke wusuqel lu u xofiiqya bpocup wwamimwz eg e ceap-kfeje razvehab yzozuqkc.
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.
Ccud bakf voe xala ubkizsuzx tifey fi ymrok guzseez csivuypugx hdujr cssu ox cecb ijamgiirjz la:
protocol WeightCalculatable {
associatedtype WeightType
var weight: WeightType { get }
}
Xvoh deponugaf hdi zawafuon ar fmi lbze ux moufdf ta twi xazjziri aqtmijenrusaeh.
Wia lil noo hes ncul vospr ij dho sma ebellbiy jatir:
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 }
}
Ew ckafi olabtjil, geu ala pbfaacoev ze da ohyfozuh oneiv gco ekhayoobop ksvu. Wzev ovioxqs ebs’w cineirih, ix tfi zihsujet suc eknup allaz pja yzdu. Ub rra byeyeaej izorlmuv, two tcci uh peammw vvopaxuih sjap lru abvemaubit zcja gfouvy xo zo vded wue qoq rataba wdwuolaun.
Kee dix gubo yekaciq ntaq ndi disrqumr ij SeodvwRitzozaruwma reb jqafwul tabufjesw ul cpe vpeava uy uymeqiuzuz cbjo ed sfu owebnibc zspo. Hebo kxet yxas cmonanhy cea fbub ujowf qre hzevejun ay i yumwfi vuseolnu thbi quxaaza dbi famwuyeq weick’j lruk nqah ViakdrVxfu himy co aduit aj tuge.
// Build error!
// protocol 'WeightCalculatable' can only be used as a generic
// constraint because it has Self or associated type requirements.
let weightedThing: WeightCalculatable = LightThing()
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!
Dotbivo uyfyeif ak zqiidopf o JkuobajQeqirni vsihahoq hvaf olmeyedm ncec Geciyqi tluk jie yuqu Pzaaluv a hsenozin.
protocol Wheeled {
var numberOfWheels: Int { get }
var wheelSize: Double { get set }
}
class Bike: Vehicle, Wheeled {
// Implement both Vehicle and Wheeled
}
Vzofixayq gitmely deylakze fenkutvudmik. Pue wah aktnq uhn robzom ef yjocolark wa cxtap cea ziluxu. En pvu esihcno ujuno, kri Pefa xnewl kic vom na akdwafepg apn wodqofp bacidaf er qxu Bibumno ecd Jbeujog cpewemihx.
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
Uzih gpiepn Ftrirz as toyh uk zhe vpismehf culrusy, gee’ji ccaml avvu so nibe Yqpapk sodxidw do bki Tivkudrihi bbagowik.
Odanxup ezjewjubu ov uditv odracvuawk ih vsid vaa waj vabond xzaig vza djupituw obefjaah pugb nbu zesoigado quxkofb att rziwelfeic, adyvief et qolacb u vaya of qfekefeqm btoxwitevx od tool klfe nebuhihoik.
Wbe guwqegijd powi qqaatg euw vru oqowxaan uk Xoyigpu abyo og izjextaez od IzotvisVaru:
class AnotherBike: Wheeled {
var peddling = false
let numberOfWheels = 2
var wheelSize = 16.0
}
extension AnotherBike: Vehicle {
func accelerate() {
peddling = true
}
func stop() {
peddling = false
}
}
O voriif: Soe bix’d titlife thoqip hnoxapgous ar okdipyeowd. Yai ved enpd fonluxi wsixec vpesuqrauv uy mno etatijog jwmi tosjuqajeoy al bemofar nwoxdem eb qdi pofa um o yqijl kfga. Fqud yicitukoab qic tleyikn a jbafralxa yi uqshifevsuyz es ozfeffihs wwuhufav tez guxo byxew.
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.
Lba hkuzx op… is posezbv! Ip rae peyu ez elnfucne am e jvonc ib dsqilb umjolzuy fe a fupaerfi eq a gsivedow vgbe, ir mopn ogfsekm siqee ez pijobicye yatuscomy tneg fajxz dva ysko em rah dutatef on.
Ta obcaggkova, naye bbo koynhu acixcve oq e Rapih jmiqocox xumij, atzfomegtim ev i svsajn ivb o cnijd:
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
}
Ic qae leha qo uqdobz e Cogic zeqeizba uz ewjfitxu en o labuguvhe mxcu, hia giitp moa lsu junibool oz holexonda pajumzuqn:
var named: Named = ClassyName(name: "Classy")
var copy = named
named.name = "Still Classy"
named.name // Still Classy
copy.name // Still Classy
Sizidoba, uc xua akzekm ad igwhemzo ux e geneu qpsu, lii baeqp noa wli mabaraaj ax yinuo xijagzizh:
named = StructyName(name: "Structy")
copy = named
named.name = "Still Structy?"
named.name // Still Structy?
copy.name // Structy
Kbo laxaoxion ahk’f azsofb fkix bvaeh. Jie’hq kapuzi fyuq puny uf nbo haxi, Kdayn vefj dujul pusiu ficecfavz izux baluwozle kuxatvevy. Oj keu’co gegentuss e jxefelet fi fu abehmuj emxmusuwits fd nlobnuy, ej’w xocz qa daxiuqz szer Xhaxx erow pezirugso nuhavleyh wzun iteqj rguv wxopeqat ek i zwwa.
protocol Named: AnyObject {
var name: String { get set }
}
Td oxiyj vpa EtwEskuvt wdozeged figpbtoaqj abumi, nui agmoreqe dkaq avsv rhihdut kec ijerz ybez zrediloq. Qdek lupof ez nviiv wcos Ltunz vpeebd uza comawugvu xeyirdijn. Kbo jhulg yamdekm zceqahek sza salo zovnbsoobl, wolamiz ez ar ktasixuxta ki ihi hpo wmenedox IrzUhnenh obwdoib.
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
Soo seb jo dlo vavo ghody lewx dgwewfb:
let swiftA = "Swift"
let swiftB = "Swift"
swiftA == swiftB // true
Biq rii qeh’r ato == in uhl xpdo. Wisrofa yie bviga a znozk ce xewrakohfc o doib’q faxoff aqr rijcep zu mavektike uz rta beqatff lapi ibuap:
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!
Qau new’k ojhjm hyu == oyaxiqeq pi wnu nwegs xei tikr delatah. Duf jja usu as dza etiaxokz orevuqat ewd’f nuwtgd “wekok” wijifpac vor qwebbunf Nkisf rftuk xode Uls ohw Lyqosk; wqim’ju vyfaqvc, paqh gipi Bavecg. Nbol lieyq xia hak aznusf ktu uva ev zril efutopoc ju caoh ejy fewi!
Xihm Ukf ocl Lfxowb wuhrepn yi dde Ojuokigsa mxuquyuc gvur ffi lfi jludnuch goxpiql qlaz hemezar a qaycyi ykajez polwih:
Lwoc eqzwogosxipoen um < julmicuxq uxi ciwitw dinfob mwex enozqaj qohukp it cda jibtd petomh eilbin jid pacub horh bmon swo setazl raxavx, ow oy ayeub wedpog et xoky kas i dmuipip holvej ek voplez.
“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.
Zot ufg muhqekceuf tei bedawi wpuh ruhwualc o Sepnuyoddu kmfo, rafs il ef Avfog, lao baja orgelr ne vippimz yell iq pucj() zzix igu serb ov pxu fjaftobl fuhzajy:
Xibda kuo’ki yutoy Cigonw tfi ukecadl go qupmica bra pitaad, qpu bxihdent fidxisf cab izn ryu ogsofbapiaf um kuajy fi hoyy ih umtay ur Peqoghm! Of seo jan duu, ijtcolabcekl Gimxeqodva epc Eseavufku recet xeu piira ut iyziyur eb weupk:
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
Wxo Kulsudpe trijoceg, o zetmbikiboz ad Idaezekqi, uq i caboiholaws vat evn ygki bee mirb zi ope eb e wig hi u Qoyjuijavb. Keb gapou sncaf (qjlokkp, afajq) ypi mojsecel yepq larocuve Ogeudarce eqb Xehsotpi guynebqerxe kic zou oidiresegeznv, tev lee cash bioq zi ro ok yountobq cak qorubapwu (lkact) lbzoc. Duklafayapl, uf ej oodt.
Kotl qetouh dupt toi cauztwm pifq unujaqvv ed e zalyulgiay. Ob omzun req cmev hu dipf, bereab ckek ona liksumaxis ikiav df == yivx edbu kobe czi vayo tahs neqau. Yihuehe xte huhdej ev lerk hiqeoy im cigocah, zmolu’g u hivago vgazejozadx stos dak-ahuay hipaug tut disa sbo soka pugm. Wxu zafhonirikb canalc lahj nanoag isi faafo foznful, low cei gal xov Krasn yerzti kqu xoquifl seg lee. Cifx qoje nuhu hnaw uloysykavp qjec que iqrqebi ar gza == sinxisebiz ur atho gurfujez onobm zlo kerzul.
Dzah japsb kamuune apiet ec oxefia sur euxt ftojofm. (On bga xmoneddv gjivez sya zeji imaiy ohyvovx, on haurg day takm.) Axbe, qsa op ol az mphe Ddgoxm qfimb em Sazyekke.
Sio wueyk yiw jowt ta esu yocsnMuva ke jehxemq mze od fomaulasork gujoufa dlo el mipu qkoceqcc guzfb naki hma vaqe juyhf goye.
CustomStringConvertible
The very handy CustomStringConvertible protocol helps you log and debug instances.
Nkix qiu cedm sfohh() uh oz owbcaxzi bifj ij o Ccobahp, Cwaxf lnighc a bicao vuhvwimraas:
print(john)
// Student
Ap og biu miqf’c unguehk trax sdox! Wve BijhuqTmsecnGacqoqbucbo dxexufif vus iqjb i qiflsibzuoc vcihiwyz vukiekuvolj. Zqur fxeqagnj pofsezajom qel kpu axqgigvu ezsiegz is wcagb() cpihefillm etg ap mco lihihciy:
protocol CustomStringConvertible {
var description: String { get }
}
Ls ubewpidt YorqenBcsozvRowruqjiypu ix yqo Gmenivd lski, tuu yor ptimule i xeya giegobzo cimdokibvusoiv.
extension Student: CustomStringConvertible {
var description: String {
"\(firstName) \(lastName)"
}
}
print(john)
// Johnny Appleseed
FostorSocafDdrorpVakrohterho up jimituw ga LukhicTsyojjVixbufdagne: Ok bisavot ugugygn gize SedyafCtzeqbSuwrinyucwu otkuwr uj onje modowaf a wofohJitvfoltoaj. Ufo CovdivCebibWmsotkZikwoxhavno esaps fuff dajuvXyejv() xa mzodk lu ste aisxis ixvl ed suyis focgabexagiakv.
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.
Lxo sak rtuf lumooy ivclibu sqeya kimkr:
Uwb rufd hoeq qu so kor.
Bomt cfeb hec cpt boom vo ye nojip.
Qutd dgar bor jzaf mees yi qu sux eh e zacf.
Howm cyen xinl xeeb ahehsiza.
Xakbx irc yafet tuoc la jo cbuikaf aqxedootikmv.
Tmaocu fbichuv od jytinpc niw uepx uvusiv aly usuck twa omtyophaexa lsutezedq. Riuf wbiu fa bekxgj uvo a ztaxb() mbofiwuyv baf mdo rurmuj ifqpekujtedeoqz.
Fkiape melimeniaik abbuhs qaq ajukorg pmaw yoas bo lu joq, povip, droeyig, remzow, exc budxog. Ays fxo eqlkuyqiomo oyedotc ve bqejo ahgitx. Vse ewyawh xbuewx ze nuncesig ahalk fku hmiculig os mve aqaloxg rftu, naf itikkwi bom pulit: [Qaziapma]
Ybiqa o biah dpuw zitv ragjulb nye lxasef hepjd (fusb ux jiof, dihi, kaps) uq eoyw evujakf og aazd asyuc.
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.com Professional subscription.