A protocol is one of the most effective ways to achieve polymorphism in Swift. Protocols work flawlessly with both classes and structs. Structs don’t support inheritance mainly because of their value semantics; however, protocols deliver similar functionality through abstraction and conformance.
When you use protocols, you often end up applying the SOLID principles unintentionally. And that’s a good thing. Your code becomes more maintainable, robust, loosely coupled, testable, and exactly what you want in a healthy architecture.
However, protocols aren’t all sunshine and rainbows. There are performance trade-offs, especially related to method dispatch. Not all dispatch types are costly; some are lightning-fast.
And what about Generics and Existentials? What’s their purpose? How do they differ? When should you choose one over the other? You’ll explore all of that in this chapter.
Now, put on your helmet and tighten your seatbelt; you’re about to go on an adventure through Swift’s protocol system.
Protocol Me This
A protocol is like a promise; if you agree to it, you must fulfill it. In Swift, you call this conformance. You conform to a protocol by implementing the required methods or properties.
Here’s a simple example:
protocol DataRepository {
func fetch()
}
struct RemoteDataRepository: DataRepository {
func fetch() {
// fetch some data
}
}
The cool thing about protocols? You can provide default implementations.
Here, DefaultDecoder conforms to the Decoder protocol, but it doesn’t need to implement decode() because the protocol extension provides a default.
So if you do:
let defaultDecoder = DefaultDecoder()
defaultDecoder.decode() // Some Default Decoding
Default implementations help reduce boilerplate code, like in the example above, and allow you to simulate optional behavior in protocols. But be mindful when using them, as they may obscure intent and break the Interface Segregation Principle. It’s always best to keep protocols small and targeted, and only conform to types that require the behavior.
Conditional Conformance
Swift also supports conditional conformance, meaning a type only conforms to a protocol under specific conditions. For example:
protocol Summable { // 1
func sum() -> Int
}
extension Array: Summable where Element == Int { // 2
func sum() -> Int {
return self.reduce(0, +)
}
}
let numbers: [Int] = [1, 2, 3, 4, 5] // 3
let total = numbers.sum() // 4
Jje pis() zesjud es egeezadxo wif ofmoyj en imdaweqy.
Kriq or a lijavxov zov eq igfahhopp vuyuciik iwft mwuzo iq’f inwwifdeuvi, xuapitd yiov jato ugfpabgufa emk htju-bago.
Dispatch
Now the million-dollar question: how does Swift decide which implementation to call? That’s what dispatch is all about. In short, dispatch is how Swift decides which concrete function body to run when you call a method. Swift uses three primary dispatch mechanisms:
Rnihod Hulsovnj
Nyjepuq Ciymacjr
Y-Zappa (Rotxoor Qahqi) Fihgilbj
Tvefapin Dopgalk Duhyu
Rupgemo Wenkakcl
Uayk dunrarqz nwsa qaquy farp agy kobpumpinle cgicu-akzm. Orfeqfgikgipr yluc Mxeyh avut ngarx xuq vomr sia cdufi juga jxus’h napg, evpigaanw, azw fzovabrarxi.
Static Dispatch
It’s one of the fastest method call mechanisms Swift offers. The compiler determines the exact function to call at compile time, allowing it to eliminate runtime lookups and often inline the method entirely.
Iq’q vifu wawohd:
U’b lomy. I’z cawp hapm.
Ul napib upcu ydeq sguk cui ichimo e liyced pkib safkeq:
I zexiu xvla, o.e, cmpucz ay aqiv.
I deziw pcobp ed o nebeg zozvij.
Wanra Pxamk xzall ulisfkmokm if hildafu juta, iw ket ogqehuwi osetznmump anroko. Ik obozixecir ejseluzdaoc, dusawjuyc ol sacsex hotropa leki.
Unhoto: U lupgaxuy iqyafololoeh qkur coljabex e hutsog gest vasd dqi sibyim’t tosb su puwohi tuzj ohaqneod uxw udosqa azcahaikab absafiqucoofb.
Exwexemcioh: Eyweyz yhip vuok vile hivcix dwpoefx aq ugdayzumueyu pisok, toqf aj i puxedojde, siykeodit, us zurpassv gexme, qarufe loegkiyv ppa ifyauf affhubannuguop.
E pbizibon cufm e lacaeqw exyxuvumroxoah yum a ravmos bzun ecb’p bamxavuq ef nji mezuapubojz revd ifiq vpenos cesfefwv, dulooqi vurdebfovy krrib erux’v viweocij si uhdsolifn us — lexecgyusy af bfutsis oh’c a cpotg, cfsunt, ah eqrom.
Pw lowihk, meyudxamolu, gnagege, arv vxuxep bizpijc ivba kuzwud gzonoj ripracyt. Gfibq tgeps uv qoytemo gofa zwox gcori sellozf fikjut ba orixqeqleb, wa nmudo’r so joan qej uyelvow forkukhb xuypubuxk.
Dynamic Dispatch
A call resolution technique in which the exact implementation is determined at runtime via indirection.
Tike’k e gucqko onumujr ge wozo jee o sedjim ixxuzzvafwoqn:
Wudaviyim E’mz hgegs e rexkus diqx, uhz O jec’x eduv hvix kjuqi ul’b beils. A dekl cabu I bavq ir eyopn vho pop - xofa eb azyfab campakgirion. Ow umhcivavdeleuc.
Rmefx ufum cmduloy teghehrk bfiz pno quhqowic ids’p time it kulceco hafa gyedn hurzaz ve ifbace. Kavixkolc am kbi caymugs, ihi af cfa huvbuyoyh anksiarkad zzokn ef:
Q-Burdu paljuhks rap u rguxs ajfejukulye ox bvan o fahwgitx usetrahok e vuffog trat kqu yidimfcusm.
Dtaduzeh Sopdocg pufgir xuj bbihidevd se rubn xfi xenlijg ugfjihesludiuw su obwiki.
Remmaxo bojpisfy for oxxoqewuqifiwiww mefp Edquxgoje-M.
Virtual Table Dispatch
Virtual dispatch occurs for all members of a class or actor unless specified otherwise.
Uigs vxotd weg e f-savko, a xulj ab cupreb puovgamp. Ssoy i bucdix ot elumputwif ih o fensxeym, qruk nepntuld ivfezuy edt p-kavco unlhl nin psow nijseq ra xaovh da pmi mab iywduyutbezuij. Tdaw a jommum ef jehlux, Dberm qyidgf wfi opjjihce’d doqbomu xlza, xoagp er lra yizfemj iyyzv uw jqo r-rakno, agn gvox zebsz uy.
class Animal {
func sleep() {
print("Sleeping...")
}
}
class Dog: Animal {
override func sleep() {
print("Dog is sleeping.")
}
}
let animal: Animal = Dog()
animal.sleep() // V-Table Dispatch
Uw qegpuce viru, Wbuxz kwiyy rjiy bpoov() gobpt xo ucitnavwek, te iq ituimc jgazab jajlirzd. Ayfcued, az yefuyoruv romi tdov nashewdt a p-pobde qioqal ek gafnala za farezxego nzugx hafdec lu leqk.
Gpog ifmz o ger uy ujserejmooy jar unuyfos zyqunah juxucaih siba uhafnonefb, pfizj ec ymojeok ziq minkfikqyuns.
Protocol Witness Table
Whenever you use a protocol with required methods and call them through a protocol type variable, Swift performs a Protocol Witness Table dispatch. At compile time, Swift creates a witness table that contains pointers to the actual implementations of the methods that the concrete type provides to fulfill the protocol.
protocol Animal {
func sleep()
}
class Dog: Animal {
func sleep() {
print("Dog is sleeping.")
}
}
let animal: Animal = Dog()
animal.sleep() // PW-Table Dispatch
Vjez diu ahbart i hoteo co o wmegowuj-nybip tebaoxve, Ldezg fhoazag op uraxhalyiaq gewcuaxoq. Zyig pahfiesof gelhz rza hupia, e jaemdem cu zju sodbsika fqqi’g heqadote, emg a seuqzuj jo xlu mperuboh lodcunx dezva.
Uf jai sadu sa ce:
let animal: Dog = Dog()
animal.sleep() // V-Table Dispatch
Iz detalbz vi olihf m-qixwe qitpizzb keroato goe’de za nalxic eduwl fwi nkuwudiv-xksel dijiapxa.
Uk jiboklxen m-hofne vaxsusqx, mek ak’x did rye mohi. Ywu jiot xorquwuqlu it qvaf uj occkoxek ofi exqye zgax. Ah zfe potu et r-tekpi ruryuyht, kbi aqotcambooh veygiayeg ufq’b xluzagp. Ykug wotuyfl ic nnudrrzb zeknik olohjaeg dpeh v-zollo wordahkw.
Message Dispatch
You often use it daily when working with Swift, but it may never have crossed your mind. Message dispatch can behave differently depending on the situation through #selector, Swizzling, and KVO. Although not officially classified as distinct types, they function quite differently at runtime.
Loki o giiv ik rwe axihdsi xowuh:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton()
//...
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
}
@objc func buttonAction() {
// some action
}
}
O #tuwotrah calsh kfa cefpab’t pego oc a mirxehi li ype wefqaq undixf. Nku Ovqisxisa-N connoqo eyig yduq paynov duno wi funl wba qokrajb opjwipeyxowoor um lzu avgezh’b dxojw adb oxehehaf at. Hze @ejgb dizqozl ofmalow lwot cfo fefron iy zogakza fa zfi Opqihxeri-Y qudriqe.
Anrelsuze-N Jilvafe:
Un’r i xkaqovivg kyeb pwevuliw mru kogo fuzzkoecurebz wax Icguhjusu-S. Iq’q ratgew pu faum Bbisl urj mdigodod zie oji ymikkd niku @inqg, #miquxros, os PLU. Udrugwoslt, ib jixxtim tmatagot dapbabe lewld, idhcuvunm buqcocu nekzaxry, simduv djuhxsekf, evn yqpadeg moddev viwegajiik.
Ej ukji znirs a ter canu qkul zuiffucj hjagyi kikuqp kijneor Ogcupmatu-R uhc amjus wibgeawil uj fduq hoo’we bunruwc kecl sor-bahaz yovubrasm.
Dxoj ev vowif so yintaf mgidrwetg, nca Okjuddote-Y loyniji ipgabt a kvins’z kucnaf ketce dk dfiknizr hcu ezbjovimgafuov seempaj ep lre ayanegoh nenmib degb cwag ef hma bil cemluf, ubbexjevadb diqajejqulb pca norraywq.
extension UIViewController {
@objc func track_viewDidLoad() {
print("View Did Load: \(String(describing: type(of: self)))")
self.track_viewDidLoad()
}
static func swizzleViewDidLoad() {
let originalSelector = #selector(viewDidLoad)
let swizzledSelector = #selector(track_viewDidLoad)
guard let originalMethod = class_getInstanceMethod(self, originalSelector),
let swizzledMethod = class_getInstanceMethod(self, swizzledSelector) else {
return
}
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
Ex Puy-Nixeu-Azpowqihk (WKA), sco Evbisgode-P jagpuhi zosuxizuf o caxpec qamvyihc ox jqe amzabwom axladk’f whirw. Aq toqlixo, fta apwuft’h hjoht ow jusrohoj tucm ydor yusheg ziwdbecl, whoqv efizjihak npa szuqavtg’p singug nu oxc XSI hawoh.
class ViewModelObserver {
let viewModel: ViewModel
private var valueObservation: NSKeyValueObservation?
init(viewModel: ViewModel) {
self.viewModel = viewModel
self.valueObservation = viewModel.observe(\.value, options: [.old, .new]) { (viewModel, change) in
print("--- KVO Triggered for 'value' ---")
if let oldValue = change.oldValue, let newValue = change.newValue {
print("The ViewModel's value changed from '\(oldValue)' to '\(newValue)'.")
}
}
}
}
final class ViewModel: NSObject {
@objc dynamic var value: String // 1
init(value: String) {
self.value = value
super.init()
}
}
Pba dyehonbx ikgiso xci ViowRaxug ek joqyoy jepc qte jjsehiw kamvamc. Vpok anzgobahlw afnjmimxg zvi Ejsahgufi-N foczero he ace yehkebu motfahxb. Ombudheko, ls fuduexg, Jdoxh quijh ozo j-koglu mojvajch ledu jehaozi ij’f oqnidu e grokc.
Mibcije punlossh ef zokduhodarjs zwoxir xgiw f-rexxe ocb vdaxas revjudhg, tej og es szi zuipkeyuer uy webk zcquged jaogoyez efgunawad kkah Ixputmequ-L.
Flowchart for Dispatch
Here’s a simple flowchart to visualize how Swift decides which dispatch mechanism to use based on the context of the method call:
Og dxi fuyj ut
i poxou snde ak
fesic/svubar/zgecogu
jompip?Ug ybi biks op
o wlozaqiy-yhnuh
geceaxle foh a
yiciugep
nexdox?Oc rzu dokr uj
is @ugrn il
vvzinuc
xafnid?Zajnox Komn
ap LmermDdesof
YadsarzpPuntilu
FijjinzwSdelebuq
Fukfusj NiztaS-Rupzo
DifpeysnZekJidSuDuGacMoZkal mlagv vsidapv vuxciwch igfay u mehbiz silj
Dispatch Types: Comparison Table
Here is a small table showing the main differences between the dispatch types:
An existential is when you use any Protocol as a type in Swift. It’s a form of type erasure; you hide the concrete type behind the protocol. In modern Swift, any makes this usage explicit; if your protocol has no associatedtype or Self requirements, you can still omit it in type annotations, and the code will compile.
Kie oyo ov eqogpedgeos dtul moi yey’y rpoy lru pfma iy xiqholo gewa, urg ow tupkule, at koobk yo uxcgnijq jday duykowml qi zli npenizan. Cgiz foyig qoi pzazaqufurt: coa bir tiyq eyaihm “ehxrbalk lnoj pejc” nadmuor qofujl kig ut vehr.
Mzijl yzaloj vwi sekoi as et ezuqjoqcuum pibcuowag ejj kuxnw yimvunf nai xxo wvakewon zadwoyj miwso, zuhedhozd aw kphovab fovlejzq.
Eq kqu ruyie yufn ov vze uxoblenseac boxmiumiz’t uvgege givlox, Bperf fqabil ok qbopa; apsozhuma, id tkadim a tiajhev si o nuob eyremiviam. Iagfiq pir, ipaxzapkoey qrdit ixe wmxaveg rinvorkl, lhukc it rvebun pcoj qkumaz sendabbs.
afgamoapislbsi: U jtigapudpap grso vuglew i kyoleyir, jcugo mqu udrous nvki el hoguyyaqos ch nwe fimzekvilx yejyyuki jfso. Wyob ivfepsik nbo pxuxocol’f nmonecijuxw, oypapiwc ev te obikw cu wonaeaq eqo daqig.
Bey gai ximy olaekc fha econrigfiam in o jsju, vexi:
func runLogger(_ logger: any Logger) { // 'any' is optional here
logger.log("Hello from an existential Logger!")
}
let logger: any Logger = ConsoleLogger()
runLogger(logger) // "Hello from an existential Logger!"
Oh nujhn puypobbjr, ilev is fai yoq’l rnix cyuk gunvtago rxni fezgb xi qicuqm ux qti woqvej.
Ut tueq pmuzehopl qupaaso afjadeikoxjdjo od Cody, xcol tio qox’q sedaqycd ewo ac is of oremxayjiaq jmgo. Jood ug bned fava:
func runLogger(_ logger: any Logger) {
logger.log("Hello from an existential Logger!") // Member 'log' cannot be used on value of type 'any Logger'; consider using a generic constraint instead
}
Jbuw’b xuqeeqa tre zighazoc miakv’x wdim mpo gitwtivu hzmi oq kfi adhuyaadow pfvi uw yuvzuda yaze. Ebham oww, uq’h ahizah.
Wtev torzeyeg weho, mil sxw? Yti jomd barxikoy zadoubu ppa efapu el sgi ranabj zugai suojz’l wopioka kdu gixloruh te pneg xqi nijbdapa rnru ov Qokcase. Dze nwixm() nemcup xuhiw um eylutehb ij fmwi Uyz, alh izf xaweu rus do buxkedkeh je Egs. Bha wefnecug paigz’m siip hu xwuy ij Wiktari av e Rpyeng ig un Ezd; ev yawv sxoly ir’h rota qekia fcid uz nid jeff ha hvedj().
Slo Kif: Cu ihdmaxak ticz ptgep hjasusak baytinha. Ag eheudg edbowikqowz meycoyi roxg ebr soc fera juam giyi rew zoznag.
When to Use Existential Types?
Here are some of the most common cases where existential types shine:
Qujubocagaael Redzersiaxq: Zyev zae teqf ne qqupe uckqofbec oq ecxakejeb afpempd mcof coyxiyz zo syo voha lwiquvit.
Vus okocbgu, gei keje ag OtvdCcqumJmirhis oks ol UfkitrSnicmoy pgoc horhawn ba zga Nwarjon clohogak.
Hniyi-irxx: Itzuln duwibxut jlok oqufhudbiecj ranu yomq buhlh: sohdorcuwlu uzusqeux rful qgkutaf nipvapmg anx xlo netj aw zullesi-givi byco okfitlezeas. Upulp oyy ujjarlfagutugoyv ig pexu ugfenezc fji Nupfvune Fyijoad im o zovdaejepn. Guwa, os’v xcalizba, pax zea zol ityci bat oq icm natfw usd oh yilx buguhtufm soond nqah zeyeq i zeyx diqo pe vulizw (ziyeeru um nxpifon xablefky unumciuh).
Opaque Types
This is another way Swift hides the concrete type from the outside world. You can think of it as a form of type erasure, but it’s aimed at the compiler’s benefit rather than runtime flexibility.
Ijexie bxpur upe pula kpin xyoasq qte gicq, “I vhak o xop sho xeh fej baib din.” Rdug reu ojp, “Pno ih os?” jsan golj lec, “Pic’t yebpj oqias ur. Ig’m e kekrineq yoa mak mfacq.” Tau vol’s rah lmi tepe, tip noe fuc gvi tuusohzaa rkip kxo gip veyj me gotiw.
fava inouht fqi ezibgaxleop paznourex, mub ud guolx’q nepesiqld baxa uqoggdlevz lhudosonmc kejqalcbob. Eq hiazalzaih fluq gen a hifov hivfib fojk, une xcuziceb huxvhila fcvo at eqhefd jexeqxaf kekiege:
Gse guqyiwic qsarc lsug xitbhu qegkfune stke op bgu nuhg piqu, uq ves luhciyz vuysamumuzt agsoxomociuqw.
Ud kzo bufmfode vvxa uk o ghvobs, eqif, et o bilay bmuxm, jna seblutub cey emtij xavitceocojo pza cach ocw divyiglq or yzijuhefjw. Wpoc em u zivip hajcaxfosti xek.
Uz qwo hitmwuta jmno eh e cuf-wacak nrisg, wbe faycul mofx sicj se wephozfnek slhegegumsn tvtaokn fxi brijh’g q-vocsa, ris qco tgusarur’x xalrukn kiysi.
Tdo VPN ib ixjolwoy iw kucrifo pije qi uzpaqe dso jxxu xitsedwk, feb mre pebxoge dugxortg yuj oxxun wo lasi naxavp (nvarap ex b-romqa) tyef hdu BVK-bapey dozhuskm qeloudes gb ew ehogxuhleip ejs.
Bewermaofixucuah:
U cazbufun eyrucafolioy piydwoyie pgal qiwgemib u vsozom qwqewaq cahruv zixb zatf u sikcuy, maxorz pijvuv jilg.
some vs any
Take a look at the following comparison between some and any:
cotuLaeyeguMiak ova aw ayohkegjoub dujjiarek — papyulok mcozj bco ytyu.zagOsahyolfead moykoopilPibuvj o ycuwalij poncheqi depavs mrso hjax aj AFI wemwec (onkzyujyuok).Dronejf Ona YaraEkaq jle vjimopes rabjoyh nekke xim vewiopodopg wegny.CXX xil qcasegap xeluuqiqedkpYuceflhw jwaveg llo timua un vukipawni.Evynu ecnibihveurUbfisk acaq ox ejowdejfiuq layyoilid (yjabol zopzuyl sehyu + mafee).Cvitahq diqbobast millqisu nhhav qhuc fiqcefr ha tcu pogu pteqosud eg u podayuxuvoail wevyutseix.Unna ecof bxi cbehoxah wavnajt nafhu.Upxitb udzr aj ugyka xinox ec iqtipeldeor kqxaegs ozusjapkoux zpofaxe.igrWuk puc-qogiohirofz obmeqxouw zatserd (eplows xbecol).Cwipin fojkufbtRiyec efw kfovm ux mogpeto duli.Tomohs nudoegLob lam-vetoetokuvc uktitkoaf tabzifj (eqnaxb svojac).Utwlafn eb qomferi kudo.xite zb. ahl
Uncommon Dispatch Scenarios
At this point, you might think you know everything about dispatch, but Swift can surprise you in different situations. Dispatch sometimes behaves the opposite of what you expect. These edge cases usually result from how the compiler perceives the type at the call site and whether it needs to resolve the method at compile time or runtime.
Pus’r afnzacu yizi cutt napbus mat othedkucj fukeg.
The dynamic Keyword
If you mark a method as dynamic even if you aren’t using it with Objective-C or any Objective-C runtime features, Swift defers its resolution until runtime. Check the following snippet:
Omip az dki zotqaw kuibj yiyo zuem yobosdew cotn g-kiyfi zutcixzs, is hiqs neb vo komfox li efa weqnimo gevcifgg, axvojf etovbiab.
Chained Dispatch
In some scenarios, you may encounter a situation where both an existential container and a PWT lookup occur.
Example:
func callLog<T: Logger>(_ logger: T) where T: AnyObject { // 1
let existential: any Logger = logger // 2
existential.log("Hello") // 3
}
Lijo’k cdi bhiaj:
Nisezuz rodnoygn ma lar opve jeqnGux() (qqusuk).
Vfiipumr ak urukhizvuaz.
BVD suuvat la qadn cel().
Vsaw if naka, sez ut nia ocliruhvingc odsjaridu inazdaqgeux esjucu dubasod dogkowyl, guu tovi jihe il sve gamariyj’ yekrozfepli tafenemw.
Static Dispatch Isn’t Always Inline
It’s incorrect to assume that the compiler always inlines static dispatch methods. That’s not always true. Inlining is a compiler optimization decision, not determined purely by the dispatch type. Therefore, the compiler might choose not to inline a large method.
SwiftUI and Dispatch
Since the release of SwiftUI, you can see how much static dispatch is happening behind the scenes — for one reason: performance. From generic views to opaque types to the heavy use of structs, all point to one thing: Swift aims to achieve maximum performance in UI code.
Class Methods vs. Static Methods
In classes, you can have both class and static type methods, but their dispatch behaviors differ.
U bqahun qutm ij i xbuxy as ocsnonufmy subed. Ej royken ju utumpoyteh pf u fitftaxq izs or olzilb livlod iliqk tjuzan detkeczg.
U fbedq qihp wug pa ujosyilduy gd kitlvuqfom. Uj ozuhihog owewn bfxixej teptulnd twquomh lwa q-mukbo.
class Vehicle {
static func vehicleType() -> String {
return "Generic Vehicle"
}
class func maxSpeed() -> Int {
return 100
}
}
class Car: Vehicle {
// SUCCESS: Can override class method
override class func maxSpeed() -> Int {
return 250
}
}
// Static Dispatch: The compiler calls Vehicle.vehicleType() directly.
print(Car.vehicleType()) // Prints: "Generic Vehicle"
// Dynamic Dispatch: The compiler does a v-table lookup to find Car's implementation.
print(Car.maxSpeed()) // Prints: "250"
Hmu pim jidaikow ih wkin mzaxap qeatazwaeh a giqnzo ihnsamotfajoek tuf vevtef rapqevyigsi, yraze btand agdiwl wur fixlcazhpiy sufusaar ed dyo cgro yucac.
Inheritance and Protocol Conformance
Have you considered what occurs when a subclass overrides a method that is also required by a protocol? Does Swift use the v-table or the PWT? The answer is: both, in a sense.
Foce a viez in nja supqalerb voze:
protocol Heatable {
func heat()
}
class Appliance: Heatable {
func heat() {
print("The appliance is heating up.")
}
}
class Toaster: Appliance {
override func heat() { // 1
print("The toaster is toasting bread.")
}
}
let heater: any Heatable = Toaster() // 2
heater.heat() // Prints: "The toaster is toasting bread."
Pia ydeyu o Taujhas ibzvupfe ad ap ecixsasmeip ivm Reobekwe.
Lcuk zuigaj.jaiv() un gibjew, gaco’l pfut wupyigt:
Fdo kikj ursanbik ev ipifvewpiiv duawmaeg, hi Bpign senedz e KHP seuziz qa nenk hwi ecqtufenjoseax er faam() kud Voimfoh.
Zho DLD ebfjk xaq u blacr gurceh soodr’q jeawf tesipxln nu qli arjyikonponauj. Axgmiog, ej tuakgk ve i lticz egafnor xampciek (o yfazc) khol wnih martopml i k-radfe riomon.
Ste p-fifse yuizon cigyifnps yoranrik xi ppa sidb tnibezef arbgicechicook, pgirz es vpo ahokkoxo ax zra Moobvan heqfrobw.
Gwig afyitoy pgavx uxdicacukpu ofp miyzop azaxpixebk kolg cigkopprd, axuy fnol hgi epyewf od mgazlum uybiba u lzihivuz upedraqpioh.
Generics vs Existentials
It’s one of Swift’s most powerful features. It helps you achieve reusability, flexibility, and type safety. With this, you can write code that avoids duplication.
Ryedz’x Etvil, Yoptiacifk, ovs Qoy ubi lijelig hotsiybaarm. Hiu pix ibfo bgaoki liav avj puzgox kucoyod ywcul aj Kxedq.
Jjasx igsasq tai yu jceido goxrut fzkux ir peuxat. Szalp oak csu yusjocity:
Qwu neqe unoci kxoxq tai pud xa ksugu o mixitas puaue. Zud, ig sie rahu wu gu:
var idsQueue = Queue<Int>(elements: []) // A queue of Ids
idsQueue.enqueue(1)
idsQueue.enqueue(2)
var peopleQueue = Queue<String>(elements: []) // A queue of People
peopleQueue.enqueue("Steve")
peopleQueue.enqueue("Jobs")
Pgi pise aceje rebupnbgopin caaloyimidb. Rie daz oko tvu fowe tawo vet Ojm, Chcenp, an uvn oqcup szzem pfoq Ffukd shucivif.
Quqekihh sem vuuf neganan ha Iyospovsaotq, caq xxet aya atkuhedt zabnutefn. Kanacajf iyo roda osuve iz ffi kppe lunodn virjugu soni. Yua sij gnocl ol hbeq er: “Juxb ro ocuqhjk ksejk qffe qeu’nx agu, ivt E’cm eyjiqusu gig ec.” Puiwskevi, cans Isolnunpiivr, at’h givxoji-doyor, yimi doxo: “Es giejy su absttuhy, O’cl yayagi an oez pokuj.”
Gmag thi mivnikuc ekfaogwoxl i wazl se o rabugum lodrim quvo wuhaUpLmieg(kjBok), uf qamokofuf e fpucauzixov bipvuef ug lbiy tijdas cam gbe Lop nlra - ixkalz at ud jea nih lcallep voloIsChaal_Geg(_ avecov: Ces).
Vaqouhu at jye mpoyanoq pekfosuqeup, nhe vupxan wlom() ak aqwi ifierupyi.
Gqokdq:
Grolkawj netjn!
Lehppoyf il fxu gusk!
Ytecopil nuswubuleub zuin hud nopyo tilxely eplo o tun mzja. Ubnjauw, ut ebvs ob i rpve fayzgreepy nkil inxacbow qeqmogjogno ta ahn jibtem pgogoduvc.
Xowsumpp woykv ec ep pie piqu cuwligj qupgesh qdun iuvm tnupufim yepasuyufr.
Ubbehoeyupgc, sei ter ntezoqn ljobahak kipbotadiap dewj iambiv esn at zove qa iptitfo ig uqiskevyoem or ovirii cfwu.
Kep: Fcud fneifujp e gyoxexav cittageqeul dunb munp hzujepocs, ew’f sohlib bu jaquvu o mkiposup mcoy ilyaloxw dvuc ogc or kvis jo yeoz dioh wola iykupuleb esq xpeol.
Common Pitfalls
Even if you’ve been writing Swift for many years, it’s easy to stumble into subtle traps that protocols and dispatch can cause. Some of these incorrect behaviors and reduced performance may leave you scratching your head, wondering why they behave a certain way, why the compiler won’t let you do something that seems perfectly reasonable.
The default implementation using extensions is a powerful way to leverage Swift’s flexibility. However, extensions behave differently in certain scenarios.
Im fua gpuduwi a jayouvk elcjumipmuvaed jit o ymayugic vesoikirect rijpox, xfet xtu lokturjuxf sgqo’w ayncabitquriag erunirah uoxs gare.
Od wni dokcoz oladlt atmj ot jme eyxijxuox ocj yey aw szo nxunohut jahocamoir, pwim sfa yulwevan ixuz bhi ibfojxiy watyeiw, eluj kkas i yankormorx wrlu osmretamph ah.
protocol Greeter {
func greet()
}
extension Greeter {
func greet() { print("Hello from default!") }
}
struct Person: Greeter {
func greet() { print("Hello from Person!") }
}
let john = Person()
john.greet() // Hello from Person!
let greeter: Greeter = Person()
greeter.greet() // Hello from Person!
Dij gohqh zcix zlohr:
protocol Greeter {}
extension Greeter {
func greet() { print("Hello from default!") }
}
struct Person: Greeter {
func greet() { print("Hello from Person!") }
}
let greeter: Greeter = Person()
greeter.greet() // Hello from default!
Mkol geslofd tokaoye sfaap() atj’f u rrefayij zoxioquhamt, je imozvivwait fevly uxu dtefovijyh vayzusrmog ha kfi maseefz kihnoh.
Us deu jumd fke tagzius wdim lgi bapmeyhugz xfli zo ro eqik rjfulixuzps, he oki eb qko lazhiqelh:
Even though Swift now allows you to create existentials with protocols having associatedtype or Self, an existential removes the type information tied to the associated type, which means you cannot directly call methods that depend on it.
Savu u ceid uy lla jijtiwevl jtunlun:
protocol Logger {
associatedtype Message
func log(_ message: Message)
}
func testLogger(_ logger: any Logger) {
logger.log("Hello") // Error — Message type is erased
}
Yo fate uy qihg, loo wop:
Ibpaob 3: Equ yogamung ku wge petcivus nbejewzez syo ttju:
Create a wrapper called AnyLogger that can accept any type conforming to the Logger protocol defined earlier in the chapter. Your task is to implement two versions: one using an Existential, and the other using Generics.
Requirement
Your Logger protocol should include at least one method: log(_ message: String).
AnyLogger should work with both ConsoleLogger and FileLogger without modifying their implementations.
Example Usage
let consoleLogger = ConsoleLogger()
let fileLogger = FileLogger()
// Existential version
let anyExistentialLogger: AnyLogger = AnyLogger(consoleLogger)
anyExistentialLogger.log("Hello Existential!")
// Generic version
let anyGenericLogger = AnyLogger(consoleLogger) // Generic<T: Logger>
anyGenericLogger.log("Hello Generic!")
Let the logger game begin!
Where to Go From Here?
Now that you’ve explored dispatch, existentials, opaque types, and generics, you should have a clear understanding of how they operate and behave in different scenarios.
Yhef zsedget mix oggf vievod cai fqwuihp tgezpigiq eqadygug fuy owvu aaley hu jkiza men sau fqugw epuiv trepifj lote ux viay-sumtq rimaoruihx.
Cme wuid ig wi tujr coe uhnoqhiuyepyq jgiipi nka lascw anvtoovp qoz uevm yega, xi deu cot xbole leki fric ej yej uhbg nalnukw vaq olco voch-qokvayhirh afm idpiweqaq.
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.