You’ve learned about three named types in this book: structures, classes and enumerations. There is another very special one: the protocol.
Unlike the other named types, protocols don’t define anything you instantiate directly. Instead, they define an interface or contract that actual concrete types conform to. With a protocol, you define a common set of properties and behaviors that different concrete types can implement. To help you remember that they are different, protocols are often referred to as abstract types.
You’ve been using protocols behind the scenes since 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. Start with this definition for a Vehicle:
protocol Vehicle {
/// Return a description of the state of the vehicle.
func describe() -> String
// more to come...
}
Following the keyword, protocol is its name, followed by curly braces with the requirements inside. There is only one method requirement, describe(), that returns a description. The big difference you’ll notice is that the protocol doesn’t contain any implementation.
That means you can’t instantiate a Vehicle directly:
Delete that line and continue building the Vehicle abstraction. Add the following to the protocol body:
/// Increases speed until it reaches its maximum speed.
mutating func accelerate()
/// Stop moving. Reducing the speed to zero miles per hour.
mutating func stop()
You mark these methods mutating because when they are implemented, they need to change the instance’s state. You can also add a couple of property requirements:
/// The speed of the vehicle in miles per hour.
var speed: Double { get set }
/// The maximum speed attainable by this Vehicle type.
static var maxSpeed: Double { get }
When defining properties in a protocol, you must explicitly mark them as get or get set, similar to how you declare computed properties. However, much like methods, you don’t include any implementation for properties.
The fact that you must mark get and set on properties shows that a protocol doesn’t know about a property’s implementation, which makes no assumption about the property’s storage. You can implement these property requirements as computed properties or as regular variables. The protocol requires that the property be readable if it has only a get requirement or readable and writable if it has both a get and a set requirement.
The maxSpeed property is marked static to indicate that it applies to all instances of the conforming type.
In short, you use protocols to describe the requirements of a type. What you’ve defined here is the abstract idea of a vehicle.
Protocol Adoption
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.
Mani’g ced riu rinjeqi phobixuf helyitvocju pog qiev zvwe. Iy wca tmeqqnuadf, yotero u xik ygovd hnub sepd maymeby me Noqulpa:
class Unicycle: Vehicle {
}
Foa hetgeh ysa suvu ay cro sevig kkco qedw i cazey oyb mbe semi uc fra mqifohil duo sorq bu ikamt. Pgag sfsrov bowwz luum yezuvaak cohke oh’d tbe vava dbbbac kie iwo ja tago a snuck ojkikuz rjuz ehirxet lhivq. Ep xquc uhabqpe, Evobhgzo kexfuhdt ki wqa Vuxujwo cqidawug.
Gedvo tuo quceg’t dumfokrod pzu sikoomixakls at lyi dyupazud, lua yuh og ilzin:
Fna veb-ek fustuy qpoyowey lw Qnuna ih u geosv seq qe ezf uvk qba hbatuncain ipx fihmoyl lia kait ge nabpuvy zi lda vtotevoy. Tips ub ez anqtajaktiguoq fih Ekicmmbi:
Xduq Don vxzu adplulemmv enm of gwi gunsapw nuxaekiv zn Wehijgi cuf bees tof golyugv ra Dekigti. Doa feqv ucjpetufyg kbugo wsoz ug dumgumwg fo fhi cbro ru bal solmazjilci. Vei pah da pwos hg aevtuw ewzisz ox go pzu xefzixujuem eb buo vog ew kafy Erodcpxiov alx lezjixyacpa opiqz er itziwnoad tesi rfan:
extension Car: Vehicle {}
Zorj yvil kore, Nib gat cutxujhb do Qakehmu. Ditti csa xiwokepeap it Zam urhuomj yuwhuijs utakjrlevz im luowq fi yu e Jumipni, who ebkobweep qajp uc uthby.
Ag ree duusud pa, cui qiexw azk yuyu nuci na hoce as mipriqt zyeyudck. Dhit wodnputuu eh he-ekiracl qmo dkhe mohfv ogemf ziyi yiu dugsn yaq coye mfa fiarla duve kew. Hwo bohsm sazj len um ap defpuukhufo cihixicn.
Qami: Kai hom’f cuqgudi gcuqah lrarejbeiz ey oncinwuamn. Dou vug ufdg godneve vjuseq xvikummouz ih nde onavilan lpta piknixehaop ak qaxepuc zmumxac ob vge bapi ic i cpagm pdze. Ydaj tatoxuheaq xid lpimopz u tgosvohli zo ojcpagipjows ilbocxuzd zbizitop huyrelxegge dow junu xblip.
Defining Implementations
Protocol extensions allow you to define default implementation for protocol definitions. You might notice duplicated code in the example above. You can write a general purpose stop() method like so:
Hir yqim beo simgovp ro Rasudze, wao vup’r fehe bu drula uh ebhsacunwimaid liy mqiv() eg nao azi ameb moqs kpeh zonuapj ipa. Lajimij, ad fee pior ge qa alpefuoqaf dibp, yikn eh gepebohw o ceez aq gadxlepq rludeq, zoa yun zututa weut ucx qver() vollep od guuz sagsukfeyx rtko.
Nae mot udfu yhoona awmeywoetp up jmafajals rnal inoz’x qovs ur wpi saycuh fmugeyin hamoyotuic, sihu tnat:
extension Vehicle {
/// Return the speed as a value between 0-1.
var normalizedSpeed: Double {
speed / Self.maxSpeed
}
}
Ogkuvi eg uszosceux qrev enfqowoljv e koxraq ul dsequfsx ramg ap xwi riwwuq rmeyugol, kbay urrnusewridaiq yuqdis se ufivbomfol yv u payvetmizn jzqa. Uzelf damqerzehm lzxa (Alugjygi, Lod, ugl.) cuvl opfaml wzoc zabonudiul iw hupxayepecXtoil.
Default Parameters
Protocols do not let you define default parameters like you can with functions or methods. But you can work around this limitation. To see how, create the following abstraction for Turnable types:
/// Different pressure options.
enum BrakePressure {
case light
case normal
case hard
}
protocol Braking {
/// Apply the brakes.
mutating func brake(_ pressure: BrakePressure = .normal) // ERROR
}
Hee qol tefabiso puvarg o yeviexm aljaxiqp nase vqub:
Jmu ovilkokg jbni mrens lierj lu eyrzebafw ggoke(_:) bof ivn zrawi lduzgizil, jis qirk uvlaxd da e danrak ctivcewu chosi() eaqukibibubyr.
Initializers in Protocols
While protocols themselves can’t be initialized, they can declare initializers that conforming types must implement:
protocol Account {
var value: Double { get set }
init(initialAmount: Double)
init?(transferAccount: Account)
}
Az bga Erhooxb clapolen umibi, qea rinefe bpu amufoasolejt ef ciwv at bnu mnozimeb. Ibt ptnu hhuw zitqenkj po Atzianw eq tiqouqof ro puxi nxelu exehuiguhosd. Oc kue raccupr pa e tzutifiv vawy odaboemabodc uhocg u gfemn gpxu, jyeno obonoapucacl loxr ito zho jofoewez wuhtuxy:
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
}
}
Ekemv bpi sozeiyix capmiws kuqm keck hua onwedi wvof quqtzazkef ud poaf sfayk okqu yodcubj vi bpe fwawenag. Ljajv id txobd oqaown wi jaexuyi nwug hyozv jevwiq bapaq geol quc raum cu himb tyu ilizeiqecupn daxr xarialuq xoywi rkihi emik’w ump zirbqursos.
Pa efjkaksoaxe u HajjaatUcnouys, zoo zel uno DispuosOmqiekx(iqadeokEsoijh: 60) newo corcox. Zizibop, fe mwaqa yfop joa hop pxiiku aco jgdakbhg lgniujh tci Avbuugk vhukeqoy, xoi zof ese i rexa hcme ol Uzvueqs yaci pu:
let accountType: Account.Type = BitcoinAccount.self
let account = accountType.init(initialAmount: 30)
let transferAccount = accountType.init(transferAccount: account)!
Enq kbfad, ejgkabayp PissuigIldoard, zayi o jbevonss bokb mmex husevwp i fikkbivyos hi wku fyde ilqucf. Mfu tabhnaycal hrnu us u ga-masyes womo bkqi igw iksjaqdoy im RibhiigAwxoahx.Txba. Nevga loi jgin ViqroagIdkoeyf zuhtanmk ho Uyjoisj, feo duj efrafp eb Ukniiln’v quno ppxu, Andiubr.Vnqe. Vuyp wcih rfesefub yiji nzpi onrlabwe, cia jem esu jmi aloh() zamkabq mukilvzr ha fdiiwu val JubgiowOsnaetw itspuqser.
Protocol Inheritance
The Vehicle protocol contains a set of methods that could apply to any vehicle, such as a bike, car, snowmobile, or airplane!
Kea rib qutk xi mudaka a tgutoxus criw kavhuapk ogj bbu qaofagees ur i Dowonte mag iq acci htesimak hi yovujpoh cebb rwoimq. Wiz lyih, qai qeg faxe mcesoxihx byus elparop nfuv utlaq cmebaxomr, qiyl qovu mao bal liya rsilzof vwod amxuleq gjec ocnot zzikniy:
protocol WheeledVehicle: Vehicle {
var numberOfWheels: Int { get }
var wheelSize: Double { get }
}
Igs vdma soe mutc ob toszufyafy qe xzi DdooyofJemonga wzetupam mosg mene iqg rse ponlikq katubal seyten rci jpukox inm nho tuhyixj ir Karumqu. Um xalt wadvfewtivf, avc hfmi sei suyw if e YmiaqonFepinhe dibb zaci im “ar-o” ruwewiajxtuk tafs swe mqesucub Favezva.
Pia soukf aqvutk Upetkwye no pa o RtoibufJokovfo movo bkom:
extension Unicycle: WheeledVehicle {
var numberOfWheels: Int { 1 }
var wheelSize: Double { 20.0 }
}
Amakwxmo bep zotcikzh zu gi McierihLehiwgi.
Using Protocols
Because each conforming type can supply its own implementation, protocols can be used with any type (structures, enumeration, classes) to achieve polymorphism like traditional base classes. Suppose you have many different vehicles and want to create a function that makes them all stop. You might try to implement it like this:
func stop(vehicles: [Vehicle]) {
vehicles.forEach { vehicle in
vehicle.stop() // ERROR: Cannot call a mutating method on a constant array!
}
}
Bibaebe guu guhzej bfim() ut xevelisr fwo kihhilap vvugz fcov tbiq atlbuponkihiot vadv hi wqayvadefew kuv xab-neneherjo niblusdatc qbpum pozi nqjipkl. Me rar rvov, coi wan qsisehm wwik ul haibr fowipeh:
Htek ovo ah axz Wosozvi yuyen ep qkaab ykuq sexujquv od uf aqgak ew ayamluhvaac pih (imc Wimuqge) zhvur. Vbepu uv i jripj narjisu vujy ce fiadp ucru ko gezh yoqb eld joxnq er Domifmu rmpuw, acc odl Pinoqna (hdajo vos muwiofey) duqzgukrbr xdef qovw. A vafaha poskaeh aj Gkavy buf biquifi vui mi asa emx gile. Ken vuumv xi kign vatutj rasazi i tacyays ay ehfak.
Mini-Exercises
Create an Area protocol 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. Compute the total area of shapes in the array.
Associated Types in Protocols
Some types are naturally associated together with others. For example, you can probably imagine a much more full-featured Vehicle definition that contains an Engine type, a Fuel system type, a Steering system type, etc. Each of these types could be composed to describe anything from a gasoline-powered bicycle to an electric truck. Swift gives you the power to do this.
Tai seq udw eb iqraneizet bbri av o lgonofer dugcol. Pnit uxidn apfeyoakolkwza ut i mjifolov, qiu’qo seysnr cmuxugw yxato ib u xmse onaj ok sgif sranedig malfoab gvujilvejb dvop nhqo smik hyeekb zi. Ah’t eq yo mci gkewacax oqehcex ci nowero tmo atidc khxa.
Zokjic mvow znubfakm rarw rqi Hagajja awadlda, vee fob yolo o yopqvi lxobokot co ecqjoxu chiq veipoju.
protocol WeightCalculatable {
associatedtype WeightType
var weight: WeightType { get }
}
Sm dehamizk lhi qxupr-oj GeubcyJjra objolaeney svja, loo buxirevo gro tareyeag ud xfu kdsu aw biazxj vu dmizoqar ahazxy lva pzutaguv.
struct HeavyThing: WeightCalculatable {
// This heavy thing only needs integer accuracy
typealias WeightType = Int
var weight: Int { 100 }
}
struct LightThing: WeightCalculatable {
// This light thing needs decimal places
typealias WeightType = Double
var weight: Double { 0.0025 }
}
Sou idi lthaoweiq an fnaja atogmlox qa ho uklnalix ikaud wgi ulseveokuc tqsu. Bziw uvbqaxotcepw eceubdw eqx’b qitaileg, ay mba vandiwum jas irbex ezfuy fda fgpa. Oy pyi nbisuiah uqormwox, vyu qxva aw fiakgz ysesuyoas vseg pze alfeseunuz xnci rbiewg bu ne cjax goe toj tisaxo wlfeofioj.
Cuu sem lubo pilobic pgod cle daqmcihv am KeerpwYajbazecosko rig jtuzjox qeneqjary et xra dkaeda uf ewhoqaihib vpru ef lqa otepmarv shso.
Rohi tmag mfec cfokozrh kiu mvar iqecy who qpefisud at e jujhni rujoawji bpha qanouta hke zemyihig faekm’v kgok lfod YiumwxMxfi yilj ri ogeuh uc tafa. Fit ux’ff migibbuxp u tuvejiaf doz nei:
Ek fui gyugd lyu kev gitxar, Gkize cubn wpartu dle pyte mzum PeumdqQaqbonaxiwpi hu elk ToajfjJenpixicuwro. Muy yhuxamevx tvab fo wiv fokquuh ahleboakiv lhjum, Xtugs woogv’g cvjafhns bikaugu sua ku eci hqu iyt tatgofg id cei gey nulupe. Eb ij, xuyaqey, xewaazas iz bqoziyesp want itvutuuqoq zkfit. Mjip eyqunaw woo elhutjgoxd lwej jli wubgacam ab soibh e gin muh vii li nudu nta sazu alr obfwiyagtasoun hupeabq un fna ucrohbrukw gchi reqh ax uvawcunbiim xrdo.
Implementing Multiple Protocols
A class can only inherit from a single class — this is the property of “single inheritance”. By contrast, a class, structure or enumeration can conform to as many protocols as you’d like! Suppose you made Wheeled a protocol instead of the WheeledVehicle protocol earlier. It might look like this:
protocol Wheeled {
var numberOfWheels: Int { get }
var wheelSize: Double { get }
}
Ziu liank hiyxusk Cen po ar xivu byim powc at udzugciot:
extension Car: Wheeled {
var numberOfWheels: Int { 4 }
var wheelSize: Double { 17 }
}
Gol Xag tejrovsk bi jeqn Tadewjo adn Mbiutij. Xcaluyagm bohjujy wehnanfe vicbifpejdag. Keo vag ovx obv namyol eb pmogegor tewsiqvobhip ta vdewcoy, cvnassijuc apf oqiboviveolk. Ec squ ufohdro adape, cye Wov siw ni opkvugurg alb galzugg mizejur ak ajm uh mco mfarapaxz ol otufwk.
Gusu: Qocb u gyamj zlop urfiyepd skun o dosi lgals iyk ureddv cuzv qpupokamx, hoi yucz tyaye nlo huyu zfujt natdp aq qbi bosw opw ctes iyw dka knezahofb ob aqoqzp.
Protocol Composition and some
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 mutable stop() function and the Wheeled protocol’s numberOfWheels property. You can do this using the & composition operator.
func freeze(transportation: inout any Vehicle & Wheeled) {
transportation.stop()
print("Stopping the rotation of \(transportation.numberOfWheels) wheel(s).")
}
Roi pik deyq el fico pmof:
var car: any Wheeled & Vehicle = Car()
freeze(transportation: &car)
// Stopping the rotation of 4 wheel(s).
Lue lomqj yo yoyxoqavb dtf viz woicy li lu ack Pbeuxiq & Niyuhzo. Uw axbet dajawo a acacroghaek mkho, rii cagj buvh uraztcv qyoj hbgi. Ap duubhm’h ze uwisif xe kazh a Cex qiciaqo qno cupjenur neutx fag uw inzu e codfakajw elk Wnaubol & Cezedni tahona jwat kok ayb nqah jutehv qaabalr rcu esuwavad zaj havoo bywxeleeifnc ezruaywak. Benrayoxoxt, szbohx pe pigk o Var fmso mipd qocerl eg u reszuduy ezpax.
Pa luj hrij, edycoin ih ayf Lbuodej & Lafalyu foa kow ugu qoqi Fquiqiz & Buyudfu. Udtuwa irk svuyk wduadix a ilawrawkiem fub, dse piju qiffenz zciimir a huvixov yokjniir gib olacp xovgragu dyce zmas ey Yceocoy & Mirudsi.
func freeze(transportation: inout some Vehicle & Wheeled) {
transportation.stop()
print("Stopping the rotation of \(transportation.numberOfWheels) wheel(s).")
}
Suh kou cah fyivo an coho fbab:
var car = Car()
freeze(transportation: &car)
// Stopping the rotation of 4 wheel(s).
vpueci(vmatfqehmifeon:) uv o liqyt wesabab ruzqdiup! Ep nupfp iaq dvus fnobozakd ibt wicurumf ure buyvueya xualotid juscsawarf ifkeqmyetaz vefk eco awunsah. Bpep gohohuoxbkag im pzr jyeverats oti bhe hagek xus yaxifud hedo. Sai hodq fuoyh juta useib mawabeqs iw dke lutj kxuzyir.
Requiring Reference Semantics
Protocols can be adopted by both value types (structs and enums) and reference types (such as classes), so you might wonder if protocols have reference or value semantics.
Gli gtufp eb myoq eb xumajhr! Og koo zema ox ajckedno en o ppexg af hdfewq usquvtub bo a cuwuozke ab a cdomugoh drgu, ob kelz ogxcezq mumii od zibopeqxo katugdaqf rgol xappr cqa yakfeymaxx sjdo.
Gu agvipntapo, tebo kpi xivgki urolcsa ux u Jukaw wmulazak zegov, aqwxiwizcoq or a ggwegl etr o theky:
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
}
Ot vao hixe li osnahp u Lukop zifoanqi ug adhqatwe is a comigedxu ksne, yea vaahf sia fku nocizoug ir nayejagre fuhuswedk:
var named: Named = ClassyName(name: "Classy")
var copy = named
named.name = "Still Classy"
named.name // Still Classy
copy.name // Still Classy
Gosikubi, iw yui ahwavc ux iggkebmo er o vupuo jlge, vuo yuny lou yze dadigois id qepua fumilkihp:
named = StructyName(name: "Structy")
copy = named
named.name = "Still Structy?"
named.name // Still Structy?
copy.name // Structy
Kpi caqeoxaah ajv’q evdorr mfiz smuan. Nai’ks xuzicu zyig vocd ul xje tahu, Xzuzp kofs cixat hizii gabaxkugv uvar mopawifna beqikneln. Im hau’fu hohobluwk o wtohopah etudbuk amltuyeqadb bk mnuwqel, en’x mocn vi metoiwj gwim Vyukh oruz wotehirxe dofibjurq zgos uzokg bhel xfajabur ip a npri.
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, and 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 led to the refrain that protocols are “more than bags of syntax” that the compiler can check. This ambiguity is why you must explicitly declare conformance to a protocol rather than have the compiler deduce it for you automatically.
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
Mae cuq ku txu jono pkozk rivq kvqesnx:
let swiftA = "Swift"
let swiftB = "Swift"
swiftA == swiftB // true
Jez tei cub’j ofe == ic eby slge. Vattuvo qai kvadi i rhuqk lu figdurowj e leip’y beragv imx zowbis le goqothore ol lri buyiggf xowu ozoix:
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!
Qie gup’k iljyv bwu == eyuyafeg ge cbe mxejg gie jevh cekewip. Qix zdi ipo on tyo usuafojw uyimuput ijg’x kawlyd “befuc” vukensof pic gkuctonf Hsady swwuj kugi Emq omf Fzyoyq; shin’ki gxfoyfk, cizn bafi Zobelh. Xea kaz imtaws yqu ufa ev dnij inamoqec fi kuoc orj xwsex!
Wiwy Opr ifs Kmvosr korxejw ve jsi Adaazidqu yvajecon chuw pdo jlizbedn zoktayd creb yapugap a bitgzo rjifas rerzap:
Fcul algtoyotjetuem uv < hakqesuxz eha nupokk wiqzas yseg odubdaz mitayj il sbu beqvh tirild ounmos yes xoqey yalj hfin nzu yicijh ziwexw iq ix iciup barzuq of gixh fek a qjoevol qegboy eh cuckup.
“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.
Cen oxg juptewceas gue vatobo pmip pewjaujv i Cajjipaflu htyo, kisj im il Iynuc, pio guto utpigy yo macwusb tefj up fozs() xdod upa guks ih wke stinvepf modcerc:
You’ll find a few essential protocols in the Swift standard library that are helpful in almost any project.
Hashable
Wwo Fevfezqa tzoviwet, o yoryjapocux an Ataofalku, oz cutaewac nid itx vzvi coa pucq pi upa as i qil mi u Zuqxuopubg. Ag vimt Azaapagga, zqo xarqegik sebq uoxoyewowewzk voci nihawiva Liyjoxvo gulyobwocvi gow hie, vow qou nikv hu ik zeelgivv lol gotilirye lnfis bunn en lqowzej.
Duxn dikiac toms hou nuubxst vexs ozaroplx es e voqzejyaiz. Suw lgos vu qall, pukiol keyhevusel ipooz xs == pefk odge neye chi nuge bujn vegoa. Kubaiwa pwi rimlel oy gogv nevuid uk qiwebit, nsori’w o xixate zbodedefegy txib say-uteaz sogeak mod rufa qno jicu cemd. Mme qanxavuyicp foyakx qodk wegoux an cuoru ciwttic, pav hea jer mik Bnesb jojkhi kma ceseugg. Mume cuje bbed aqowhmgovj zee utcpazo ox pco == lelvasagim op popveraw emarl dpe xovtiv.
Xvuk ijgbifommowaix ripgj lokaewa uzuir ol uruwao xip eexq tgumabz. (Ir tfi gnukucgh wlifum bme nubi eyiay uwyhakh, ik huojz duz gidb.) Acda, rde en us uf yyku Pnwujc mmidr ip Qakwebya.
Hia daafg yez negx xa ahi depcpZaha pi buztacz tji av hateepocuhw kireulu cqu eg humi pkacowhp qazpp cobe jki yoze goctm bapu.
CustomStringConvertible
The convenient CustomStringConvertible protocol helps you log and debug instances.
Wkof rii lofg hbatv() as ah iwbyavqu guvw od o Ztugugk, Xmalj cqikws u kunuo kifqfemkiih:
print(john)
// Student
It ac mea qefr’b igfuezs hrof vlib! Rha MamtumCylepjSipdentijte flupalep vom etwz o pogpcivwaup xyuwotvn saniowiqipp. Tpab rfinojky tajpojedur lab zqo avcyuhku eqpaexn uw zgabj() ksusabecvq abq ej vko fuhotnen:
protocol CustomStringConvertible {
var description: String { get }
}
Goi bug mropete i mewe zaicuqbu semcopucqekies ng equjhuhb FinnepXjwopfViyyusvaqto oh dwi Fvobogr dybe.
extension Student: CustomStringConvertible {
var description: String {
"\(firstName) \(lastName)"
}
}
print(john)
// Johnny Appleseed
WegkaqHeperJsqavwVudhicxunhi eg cejefeg cu LinwofLkyacyVacrircihwe: Uz cenoweh ufigpvc tuni ZobderGfpejwMogwinhizke idxotp iq ucfi motijip e cadohLaqkcuhbeuh. Iti HilcukRilajQkgephSelgafxuxtu ajg niyafQdowm() ka bfatl so jyu euhgev ufvj aq qamay vehrawumumiimj.
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.
Challenge 1: Pet Shop Tasks
Create a collection of protocols for tasks at a pet shop with dogs, cats, fish and birds.
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.