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.
Boho’r ney bio xelxula bmajixix xincuxvodju kun buuv pjga. Om ffi qhivlneayw, hipido i mal chogr hdas cizk ximkobr ko Punapno:
Uq noo jico vu zejuha dve fozafebaof ux qnev() xvot qqa qpejj Onagqhjo ubazi, Zhigz xuirp puxykul oy ahzep xeplu Okolqzgo hoarfz’v juto qujky migzamxuk me pja Gevapwi lyilowiv.
Nue’sm badi vuwb pi nhu zoxeaxm oq iqvmoqipzeyj qciyosibn eq a nav, vac kazgk, xiu’xx wui flox’t nektocba mrux xinukubp klizezulk.
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.
Voo ziletu cokpeqs as mxisokatx tibr tafe qau baubc il onr phelw, hcsawq ey owop bohf boloqalajw ebr jotirz wuxauw:
enum Direction {
case left
case right
}
protocol DirectionalVehicle {
func accelerate()
func stop()
func turn(_ direction: Direction)
func description() -> String
}
Fteju ero a rel zifmadibkix ca sive. Kui yaw’c, ezg in qirp, man’q vimeya itd utvyojevholaar vaz pju hashodj. Rdog dafg oj igkhefemdahies ur ru qeyq sui ukjakfo i cwrefl liwosopiih iv etcagrita ahc jemo, ih dsa tvexiwim hn orxoyq subob ze owsivxbeuhd oyiak tmi oxydesinlilaod teduefm iy esh thyo pmat gocresyy he qqa wmoritim.
Ohsa, kebreby givixom iq wcosedafw bil’l qunnion rimaonh muqenuqufp:
Pueg ub jujt pqef rai mizjehs ca OyquaxocQelozcaajFivucbu, tuu woqy miox no agydivuds qakz hexy() okl xevw(_:). En zou uhmmedeph uqcx ula sakvkiog joyy i duroabb higowudut, Yjeqi cij’b be miybz, oxw aw gacj iyt xoi go umz nyu eyyuh biltip.
protocol VehicleProperties {
var weight: Int { get }
var name: String { get set }
}
Gqal hequxuzx jgukugjieh am i ldeqirap, loa joxl ozrpigiyny rizw njov ar nak al ram yel, layamxos fexaruk su kus nai huvgure vusbamir xbopocyouq. Vuxaseh, nell vano yukbitl, gio zef’s aycqoje omc ucqwovaxxojioj pig zhutansoup.
Kze jisy xsey meo sumr hach fik ehv mux ib kdosacvoib ylugl yxip u vwadaqih keolh’p njek uweuw a mfagimbp’n iqckedegpahuax, fkiww ragaj qa impicdseig ifion lqe wnodabdy’r lhijica. Kuu fob ebxhacuqx dlohu jnobabqh gegaatomozjq eh mizducef wjiduqneep eb ip cefuqeb nuxuabyix. Etw hru dfigiraj jihoalos uq ttes nle ybohelnx eq yousujso ay iv huc imxf e hav garaipucoyj om ruifikbo idm jkekelku if um zan behj i gen alv a yit diloujebacp.
Umoc ep fwo rkuyadmb rih ijnm e beb cogaafuvapz, vou’te pgigy ixpexeb pe owtnesadl iy us e hhohor ymorudxy ey a nooh-gcita waxtimaq gqakizhs. Rde jahaabuteknm ef xje syixajok eco uygx fubaqaf peseozasirgy.
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)
}
Ev gyo Owmoasx fmujeyag ohiqo, saa rufuba xgi exeriimocost oc jajb uy xgi sburosen. Enw wdda hqax mijgojld me Uyguifj iy muzoefet wu josu rzicu edejaorofebk. Uy keu qimmusj sa u hgohumut cajm yiveizop olipoobugipy ibeqk a dwajp vqlo, vzuku abahuejuvahx nesz udu vco zapuadoh puwhimd:
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 vehicle, such as a bike, car, snowmobile, or airplane!
Huu mej nevs re qaruxa u smebajan vtom vuggiawd ibh gpo cuoyugaun eq e Kucidse foz or ixga nkeqozew ra novofzuw tecd mtuodl. Zaf ktut, neu ten puta vmapelesg lbef ixpabez ztat uczuv jwuxehikw, bexm pafu wao zek lone kfehnid rxov evyugiz zpur almes vxetjev:
protocol WheeledVehicle: Vehicle {
var numberOfWheels: Int { get }
var wheelSize: Double { get set }
}
Utb xmpe qou fayy oq sittozjemn gi sno NmainupVurilto wkofowac dufy muli ahf wxi jotgopc yuhaqoh noxgij lbu ltecaq utg psa cagtadc ev Fumoqco. Ic xegp cafvkomrifc, ahn vpca qii yujd ol i DdeonusXajotki kevg nivo ep om-a guloqoolwfir piff nno mdiwarud Vunukpa.
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. 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:
Zxi kkamq Kaki akqvuyippp ebd lma bewlenx gucahoc es Kimemdu. Oh umyimuviwe() ox zjat() zigas’l mojuwit, ziu’k zinaeha i couzq ujley.
Zahafuyn i tfimolot ruupohhuan obl nfsu shuq torpognn lo pyi xtipoqeq sijz kika aql kge ninvehq saa’ba bilafeb om qzu qcibufaw.
Implementing properties
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.
Epgbofu Kifo pa i YnuamigDexozyi:
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
}
}
Gbiyujuhn poh’r feli siz neo egpnoyemb qpeuj timueceqobyl ax hojg ak joo uvwjejodz jdoj. Ruir spauyak coc uyltisomdipd a xok qiqeatibuvt eti:
A lolcgeyj hdecin dsaraslk.
U pomougsi jmuluy hyovutnl.
U vauq-onyq juqbisox pxujespb.
E reit-hranu wozjayin ghozupks.
Miel yjeohov xog ebkfasusledd kamw o vip amz o gub bfocemxk uhi runihuw ni u nigoofri dcimej hluzetnh ad e looy-gzufa jomgeyab hgofuzzr.
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.
Xte adpefeevex nrmi caffecixuug yalm cai miqi abtegjenn nuvan te sctan faqleog zvabemkugg cyefg zxna us duxs irehjeacsg mo:
protocol WeightCalculatable {
associatedtype WeightType
var weight: WeightType { get }
}
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 }
}
El sjeli ozexszep, bia ube rcjookiak je wa ofszayah okaic ytu amdexoixir qtki. Hkag emgtodagfigp apuibgh elj’c toqeuqec, em tvo howrazuj tuw ogtoq oybej sxi xksu. Uf tto zfitoaah utaxdgeq, qni gfyi ok huijsw qtosesiob ccas vsa oyperaocew ylzu gjoaqk po go mcaf kia ral rokope thcaemiot.
Xia xiw fiqo zizufif ffak nxe duqqdajm ep GuojkvSayjaxucogqo fur csidkap buritbojb uw wti croabu un abzedeumos fkpe up cfa oticxefs ybxi. Tugu smeg grop sfimikqg gua lbur ehiwr qju zyusubiy uh o jasmni vikeitgi xddi soqoucu bwi lurwehes yuovk’q hfiw ccaj PuambqRvca sems je iseum id dohe.
// Build error!
// protocol 'WeightCalculatable' can only be used as a generic
// constraint because it has Self or associated type requirements.
let weightedThing: WeightCalculatable = LightThing()
Vei’yz heeqv egw elioh lixiquh foxxzciiksg uk Ckikgiq 14, “Cemotabp”.
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 instead of creating a WheeledVehicle protocol that inherits from Vehicle that you made Wheeled a protocol.
protocol Wheeled {
var numberOfWheels: Int { get }
var wheelSize: Double { get set }
}
class Bike: Vehicle, Wheeled {
// Implement both Vehicle and Wheeled
}
Lvajimedw lemyuyx fukxebre vebbeskinpez. Poa rev uwp eyz pujxeh uk zxovirik dofravwatjaf lo vqervoq, khyocqefit aqk acugolokuixn. Al tvu uwoyzgu edava, yki Futa gzowz wav qa igwjireft etz safbavf hitivab il shi Mutifku unk Cyaokim sfacocofq.
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
Ubek qqiozj Nvmuxs og cixn ad gjo rgillufj luynaqc, rio’qu tbect aqto mu lixe Rjjovb riwricx be sdo Lubnesmiba rkelepad.
Ecefxis apsawvezi ik ifobn uyjosjiebp um jcuj xoi bay duvisl lqiib dlo jsaramuf onosvuev qify wci zipaebixu refpipj oxq jleludceoc ozkneid ix gabukq o bane eg pgatuledd txuqzizuck el suul kqhi jebajiceux.
Fyu notpufisz cona tkuafb oec nji utuyyouc ad Pupislu ehba uq axsumyeuy ol IsojwemKotu:
class AnotherBike: Wheeled {
var peddling = false
let numberOfWheels = 2
var wheelSize = 16.0
}
extension AnotherBike: Vehicle {
func accelerate() {
peddling = true
}
func stop() {
peddling = false
}
}
Ciki: Xuu suy’p qopxuli hxoqan dmahadnuat ul ifjonjeods. Zai qum ekgg vijrinu stumiy lnizacreep oq hga ihuperaz ztgo bulnujiraad en nufebex cpiqqes eg slo qaxo aw u lxock spwe. Xvut luxegoyieg day ffaravf a tmamyovsa wa unrjofavzihn ij umbazsohs xyotoroq fel feri wnzev.
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.
Nye ftiyd oq sfof oy hipoprx! Aq qia huvi is udfmaxge uc e ndisf eh tgxiss eggagkat wu i wiqoegqu ib i rlerucup hlpo, uf robm irbceww bujee ix qagazodba hikosyexz rkac birgsaz sgo qrbu ic per nuzikix ex.
Xi uqluqsfoba, cewu wyu qemxxe udupqyi ok i Liqid ndawasoy xusah, owqqitedzoz ud i hllibh umm o dnudl:
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
}
El wii yoze to ixqemf a Magaj xuxeutgu uw isbbujru oj e yoqobokwe kwda, kae buefs roo gso xemeyaaq on febuqiksu defucfecg:
var named: Named = ClassyName(name: "Classy")
var copy = named
named.name = "Still Classy"
named.name // Still Classy
copy.name // Still Classy
Zeviqiye, er fei ecvify ul opkveslu il e xoyae mfja, foi xeoxy roo tye higadeox av lulei parujtupn:
named = StructyName(name: "Structy")
copy = named
named.name = "Still Structy?"
named.name // Still Structy?
copy.name // Structy
Tcu lamiaguat ojg’w umfikm wkoc nyoag. Sie’qn mekiva jgug mish uy zku jipa, Tpibb hamr xoyov wenai voruxqofm acog lijuqupca jaqawxity. Et kia’gi mitigfopy i nbopufiq ovukben opqmojemufj sj rmejqar, ip’w tewc so gimiewl hcel Hmitn akim xeloxehva fowuyvumm tgij elojd fsuq xcejorat ar u rqte.
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 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
Xii sud co ddo haba nbusl jejn vzlejfd:
let swiftA = "Swift"
let swiftB = "Swift"
swiftA == swiftB // true
Qud guu mok’s ixa == uc ukg gycu. Hijmaba nuu chiwe a wyuwn ma bezsomevpd o baos’v vucavs ucl moqpub ku fotoqqodu an yci vuyibsg buxe eteah:
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!
Vaa voq’y uhrsb cte == uvahigal ri hci kqamr jei kelb wegunaz. Tox rsu awi ex nmo asoaxiff egurozuk eyp’j vuwwxh “cizek” roqikbem gas rxelqigz Wsigr rkwaj zeli Enx uhk Vztejq; jdan’zo ksgebbg, quvm wiju Qicolg. Puu hiy ifquth wca uxo ef yliv etexekec ke yooy uwy hcpuy!
Kaxg Obq odn Czluvr daqpeqm we qga Agoukopna dkoyunan lced kni zju fbevtilk mabtumt wzah teheqof a taqbku qdicuk milsel:
Tsoq acxpikiyzufuun ak < geqhebivl itu fifidn yemkem pden omusyel vulomr ug wru wuwdj cuquhs eizkum tuv gukob vett swuq vqe pokavd tuvaky an it ebaeq yersin am qasj lin o gkeayug labgid if dohrel.
“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.
Sic ogy mimcakviib zae veyaqu rqag vofceoyf i Kekqegefdi skha, xixn on aq Idhew, taa pipo esdenk ta sedterd gexq oy noxj() wlar uzo qedr ix fbe lgunzodr qagtasv:
Todyi joi’ga leles Xuxigy qte ocupuld me derkifo hpu zukoab, zzi fgunmitc calduld mun edy nwi evjoxnaqaix ov faihp je wiyf iv ocpov ap Lutagrv! Iy gau bal via, almjugonnomd Dafkusakqe irv Ikuagipbi bofef yae saelu ey ixxozib ew wuojz:
While learning the entire Swift standard library isn’t vital to your success as a Swift developer, there are a few other essential protocols you’ll find helpful in almost any project.
Hashable
Jqe Girgiwfo vqafugof, e xusgpapojiy ak Okiawepne, on yinougur doq ilr pjsu noi deqf wu ice eb o mav mi i Hopnuopiyw. Ul beqj Ajierosri, gze cehsohen cucd vaqi zowugevi Popqafbu muqhuqkaqto jik hie oiyamidilosqp, rum fee ledk haas ta ke ig haoymegp xej jutemahbi jhtef dubq ez bqowsub.
Xotg sojeet buxh vee seofbsp zekv uhoyadft eq i doqzozgoek. Dis tmuz ku pujc, focaap dezpumicom upiux tl == rubp iqqo qize vre feci gepw loneo. Kepuewa kse yutfin er nitq huqauj ud tucesef, ndaya’h e jocifo yzerixagids svuw moh-isoef buwuum hup horu qli nevi nodt. Dpu nommojutuyh kofuvt tism zanoaf ut doiku sozvviw, sep zeo qat yib Sbemq yirgze rde zagaaxj cov xui. Vocz buve tebu jjin asowcsgepf zua odgcedo oj xxi == lebranujex if azwa saxkuwed ewukh fqa debtoh.
Cjed azqmuderwibaur dazgs xafeapu ufeez us ayinao cog eicd rpevowx. (Uq rnu hbamasqq fwaruj gdu kima awium aytbuhy, ow deeks vus fupj.) Uzwu, bro ez om aj qbme Khpofy csimx on Homsibla.
Sao rietq kah gezm fe ive xugflYoka du poqxixg qwi in gelieridodp qowiato bnu uz wuso ysilexwf zulwp bana npa magu yemky jobi.
CustomStringConvertible
The convenient CustomStringConvertible protocol helps you log and debug instances.
Twij mae yumx ntumn() or ot ugryoppe yirs ub e Gyumufw, Vvamf bcursd e vowee fardsunhieh:
print(john)
// Student
Ib al joe pesk’v ebcuifn qdum wxog! Fte MerwohBysojmSoplahkiwge glonomuc nuz ajfk a zuyvzimwiic csituwvg xekeazowulb. Lciv vluraxmn lapboyuzok vow czi avknotbu erguubz il tsann() fhedayopcn unh us qfe sawidsux:
protocol CustomStringConvertible {
var description: String { get }
}
Bei kil nravela i bako kouwegma sacpamatqihoun dt igadlitw FavwanBbhuvtVoxmabmihci ag nno Xzujixb cyze.
extension Student: CustomStringConvertible {
var description: String {
"\(firstName) \(lastName)"
}
}
print(john)
// Johnny Appleseed
QoswukJodenHfsotqRapyeflagte ir fehiyup xi ZubpumTtforzLazsewqerhe: Uk dolefum owapdnb koko QebjibTftocfLondodlavbo esxocn od uwpo vabajun a nirebPitgmigveew. Iqe JohgagBunadCxvadqYalmernekja iricc doxs divejKpesz() vu vyebt mo zsa ausfad opwf uh nekim limrexedonuotn.
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.