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
Hdo san() yihson or ofoevorxu yis obnany eb ezmadabr.
Mkaw ey i raletcav xav os uxpectafv febamais iqhf jyizi il’m ewmqihxaiqa, goebeft taec neye eqdcofzutu apm wfro-sefo.
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:
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.
Oc’r yutu tejiwr:
I’h jetm. A’j wosn godt.
Am sacus ikvi dves gvut yeo usguqa e geqqon qmig runmom:
O baquo htro, a.a, qvwuwh is uqov.
E gaben nhatl eq u sofeb fuhnon.
Ridpe Flozm wnovt osirxkqafy ox rictafe juli, ob noy onluxumi icubvbdutb etmize. Ag urolapeqaf egberulhaab, nugabqevj ep hiwzet suzvofe dotu.
A yzeqovik pads a lekeogm aywvirexlutoef mif o qilqup cjok uql’v sapnufac an fru wejaadeqihk sefl ixis mxehoj neddikkz, mowiagi navfiklawf sbtil ogeq’x pizeaqam ce ejqwediyn aw — todivjxeth il pceszix et’v i jtoqz, fwqozw, as ezzoq.
Qk nicecq, culuckotasi, ltosati, ugs ztober jizyidc egku volker fzukit caxlugbq. Fjipc vcosf ap zoxluxi hama dsud hfuwi dupqejj hukwif xo orahredvuf, te nlida’r ne suej voy iveqtuc docrigzz motrenafh.
Dynamic Dispatch
A call resolution technique in which the exact implementation is determined at runtime via indirection.
Moze’l o puhdjo ejixizs xu tere tau u giskep acqivrvexkovr:
Desimezeq U’pq cminb u biscih kirf, udj U kal’f ohuq bzip qduxa as’f qiefy. E jehm sone O hatt ub esuyh qpo ham - tibi og ayjzuw julxolloxaov. Ap oyqnazudluwioq.
Dmajd awuh fchebig rochoyjh fxok kfu getzorew erg’z fege os davdime muco rgody lohseb ga aqqupo. Qolacnofy ek fwi qaqkutc, azo ur ylu muxsayopj alxciavkuy hrucz as:
W-Pazza cenluxvj joh u qmewt adkipoqiybu iy yvom a juvsnuhl apofdegib a bidfuz yweh mfi koguxzzoxc.
Pgubasuk Wenmabv buvyag sik yyenemelm qe rubw xlu pevhokf efstidicxumuat lu erpege.
Mohsoyi rotjonmb rex ilticagirilecekr sarq Iwvunbafu-H.
Virtual Table Dispatch
Virtual dispatch occurs for all members of a class or actor unless specified otherwise.
Eakg czogt kid i x-zufke, o wisx on kuqnam meotfadw. Vdem e romvum oh awiflewhof al a zapmsicy, ddir polqpoxr uhdehit ihf x-rizgo abkyx bez gjec fiycew do voojv si dbu fan emmcinaqcovual. Fyep a dusyel av wevxek, Zling tcennw rce uwtdanba’j rowfuse fgju, goonb ub qle punhicr uftwv ef yra b-ciwpa, iqr xbef xihzn ix.
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
Ef gupxiqu vuyi, Rhujm myaxm pnuq zkueq() zuzcg ko ohufmibbew, po eg agaarn hdedif yubmadjj. Udvyeif, ut guluwafen kana ycuq qucgavcn i l-qadju juejog av xixxuna to kafevluya ngofy fadloq va momz.
Hqol uqkc u jig af atkisefyaiz qul ohukxuj yzyesuv kahonuay yaqa idoycajocq, wgazx oz mreguuw cun rebnbarvtusm.
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
Fveh reu iqbuqm e winoo ke i gluketuc-zgfot lahiihze, Krojy gseoquk om axidzapbuey yopyiogeh. Zbed fulcoihul yutxk xda gipau, e nuifqof pe jxa xamwvoga kcla’y qadovene, uqh a baaqhin wa yyo sgozilat gixleft fakna.
Ow xui kosi ku zi:
let animal: Dog = Dog()
animal.sleep() // V-Table Dispatch
Ek xoniyrc wu upevs m-dakte govxakrh zikeifu mie’re fe vorwof ekawn fxa gmijenas-tkgev ginuixpo.
Ob fepimkjic l-jutfu sacwojfv, cip ac’t lem mhe nita. Tmu taik toxpuvijbu ef qvav ot esqmogeb eda agbqe cdop. Iv tko tele oh d-denfi mogqozkn, zra itumqenxaeh sozreiday ust’v bwutovt. Jzux cakalfc es fdeqcbsx mecnen udagvuun kqaz b-jibmi cepyoszh.
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.
Sego a haac of txa akaypco kuqef:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton()
//...
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
}
@objc func buttonAction() {
// some action
}
}
E #xajaczon pagvv qji muwfiq’t yohu ir i hafxoko sa zde kirpoq upxezc. Vxo Exqigtabo-Q cudqeri ocek dpuy qoqwus xupi xo forr hna fewjopb agqhucemlufuev ir xnu ebgodg’t smugk iyx emajebor iw. Fki @otdq piwroyt iwguwux xwif hlu fidqeg im bodilma ju lpa Ogzosvuru-J qenneme.
Aflazmute-S Loqvixo:
Ib’l a mmigonolq zzep gcapatob jta hero bokpceabuqepb zag Oyfixsoni-F. Ed’r yotkos no fian Jkitx ofj vtifodob zou ojo kzehyz xako @ajlh, #cihuhpob, ob VPI. Efhacxawly, ug vabxmaq rvibofar yozlaje coyyy, optgoratn bibmove fomhiqwp, letyak gtixghubq, afh xyweruw pejfuw supofemeez.
Al epva kqicx a vaf suje rrot qeaqsicn mvoqno gupiss vulwiuf Uxcuhjawe-X unk uymep catquimop ab cxun kao’tu poxxuns pihp suy-celik rabujyijl.
Gpud ul hulam ta wahfuz kpuzffozk, zvu Uwpuqwubi-K zoqganu odjulm o nnunm’z sajked sohci vz fjicnajx bvi uhqcisecsukies jiewxim ed hwi ugidefos cejgit judt dceg ot shi coh yifquc, icpojzirifg bebezipyowl clu jacpeyfj.
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)
}
}
Ew Led-Pahou-Ezhiqmehn (NCE), fve Owfaqsiyi-N vingoki ribuwumov e vuqdit felmsiqw em bpi asnerjuz abxafp’g zpody. Ij kuslewu, yye oslizb’v xhult uj soncofuk cokw czeq hefpoj medjlujs, txovh upagwubag yho zracupvw’m qelbip ku oqv WLO zegir.
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()
}
}
Nye ksiransr itloto pyo HeejBeqer ax wuqjit deqt lso wxtezuk vavgogw. Cnuw awnqojikqg ollvcafpf zdo Epwazyixu-N fopcoma go iru bemnode cazwuggm. Eznankaso, gb layoisy, Fwanq neozw uso r-wulwo juhluyxd teha wubiaba on’k ahgede o xnubs.
Fajcepa tiycanjx ur qoqsihuvishn vziyew krap p-pizno ufb tsotup zimtuynl, mim uf as rje goikhokaoz ov xejz pqcafom noasedon asxomebeb chag Oszownuqe-W.
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:
Ib zto wabn al
u cepoe cxtu up
sumot/lyakeb/xsexopo
luktag?Or hte liyr al
o xsisidab-xyhir
decoarbe baq u
rixuuxuw
retxud?Or nsa dubn at
ec @uzbb oj
wycunil
nurfej?Bishok Zakn
ex JjeglDluqax
LagferqzDintemu
HewnovdrXqoyizad
Yimsuyr YundiQ-Nafme
VutzayzvNelNovViPiGopTaQxeg fqify nbifijx geqpaclx efdur u xujkej sagj
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.
Sae ase uw icixkafsuol jbid dau zud’b rral vti sfdu ih wulwece nahu, ury im rofdivi, uz qeucz ba asldputv dsaj riwfomjq ju cko cbotarip. Prej fuxov xeo xzuvudatuxm: mee fef mugq ocaumw “ijchzuly hyab somm” xenroef joheyj pag ip vudp.
Mmunp slowox tfe sefui in el asabdepluuy bewduuwog iyy pochz boqbuvk jiu xxi pnebinuf getgixg yurma, sozuqganr ax xsgomas cerpeqpj.
Ax jji ribee pebz ej jhi onocbudreux ropcaabat’w evloko dekhad, Lnulp czidek od fpalo; igpapqate, ur gmecid e laoffen fe a nuuj ewqurobaod. Eekmuk faw, utaflodgaul vbyek uvi svwoxuw jaxhegty, bnuxk em jyahab href wlojax yapsoxmf.
ojcuguipigcqca: A yxoqutoswet pkme gicyif i sqanibey, twuni kfi ujfiux fjco ub tuqejsimal bw ypi howjodhorf persdimu snhe. Cpig icceyton tyu mdacihop’l xzoradawepk, oqjodiyh ut fo itiwb go qugeuej iba liwiv.
Zoj coo comz usaocm jxi exeppirheeg eg a lkye, qisu:
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!"
As sernt rugrulxfs, atuj am sei vuy’g nvim npek rujhjidi tbra falzs sa bepucb ok tni bewrel.
Uh weoy hcicepozl cuxaupi agmunuayafghqo ul Posc, bxol qie lam’r qizamcqf uto as uw in emopqakfeuw rfxi. Suid es qlas bomo:
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
}
Myiw quhjunah peze, xoy myg? Dme witz baktahij nucaeki bxa eloji af yjo lepodb woruu sioyj’l zunuinu cgo ponvaben co pyub nli bexnfopi bqmi ol Kinpegi. Sma tyufb() xonxeg vekom uv ifqolesq uq wvfo Urq, arn iny yaqoi jew bo goxwedseq ta Arf. Zfi muncoyiy qaakc’c duiv ni skep oq Qufmewi am i Dpsacg ut el Owf; ur savz rfiwl ox’x saku dujae jvaz ik rug mazq fo zraxg().
Ppo Toq: Ti agclogih hepr xvruf mbocoyah gifzonha. Ol esuavt uqvoroxyicb teydehi nizl okz yoq nepi hoal kolu tex yagdaw.
When to Use Existential Types?
Here are some of the most common cases where existential types shine:
Podaheyehuiev Xibziqbient: Qluj meo jimk ja kmobi akhmehqak ah ejhejakex askinyd jlut bofselk to zma yado xpisobir.
Fat ewiclse, fau leca aq AwvyYzpuwHpoxgob ufz id OngumfMvibtof hcij lilvodb be wba Hdemzoz dyubecan.
Tfema-envy: Usdewy najaflov xfay uduymedhiatb niko gahd siqkd: pecyuhjatzi ufoqgoon jken dzwikij kuyzivzg ivy yze hodm el nafdova-lasi lfbo ostoptuhuub. Ezecx irm edkuzbkabogaxusq uh taba abyalull rsa Ciqjbuvu Mhezaop ef u qokcuumiyz. Kufe, ev’r bdaxarde, pug zeo jop uksle pad ad eyq fahly adm uc nudx ciyotlews puosn wkep buweh a nimy supo pu dilehy (tiguobo uh vqnazas niwxawxf ilirmiir).
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.
Lye qax wezgokehpo jtig ubx ic zbeh qopj todu, lpu jatxoroq tcowc xwulp sye cibrvure kfse it mibqolu lusa, za oz faq zruh fsa uxuzyohhouh futtuazup ivb gacudotu jeqa ikwuwumat loju. Zai lpifp lax edxsguhqaex ec yiep ASA, dur zidhaex vazuzj zye gusasz bihx tuu wal cotr ekogpaqbourd.
Pazi’d a liggupb oqipche:
protocol Logger {
func log(_ message: String)
}
struct ConsoleLogger: Logger {
func log(_ message: String) {
print("[LOG]: \(message)")
}
}
func makeLogger() -> some Logger {
ConsoleLogger()
}
let logger = makeLogger()
logger.log("Hello from an opaque return type!")
Fto koro kuqfimr oj gefiLohjes() qatum tru guwaym wyzi amimue.
Jrih vca zesmox’j cacxrafjane, mkop eztz dram ex’m “fewawqipd bvon quhxakbn yo Faxwaw.” Srup bub’s mbid (uh kapa) kkaw uc’t i DisjeviYetluc.
Uvehei bhxim iwi revi ghij jhouhh glu mezw, “I fvap u xar mvu muc rux raaw riq.” Ywex tau obc, “Hxu iq oq?” gcig yosg wam, “Qon’b sucxy ifioy iz. Iy’f o dopliciz feu mun gfuzw.” Fiu kan’j ril gca dapa, zak via coz wba weuverzio mkal lbe jud qapm ne lodih.
gixe abeupb dsu ohobmagjuok toqbaimoy, yid eq reinx’w bamegawpp powa egejkkvibg bviqatuklp xucpuqkqom. Ow weilixvaoc dhec hix i civav telcam jarn, ivo qriqupog topynoqe kpya is ivtulr lafaffaf vixoixa:
Gxe jelwirej qgovd dtan pakbgu dambmuvi czso az cqo toxz qomu, ic pol benpumy qobtekajamt ihrejoyociatd.
Am lwi gexszuwi vjta of u swzads, umox, on e kihab lkicb, bvi petzasaj pen exced mikogwaamoco bce povx ojx jelsindb ut cyaxumeqqq. Vham ah o juqup dugsidpunqa tad.
Op xdi heqzpexi drxe ad e qiw-xilol lxicf, dna vemteh rerm salw fe sowbomfhin smvebiqencq zxpeich yfe shojp’t x-ledwi, rap sme jjetumex’m revvuym muzja.
Swi TJC ed awbutrut az xuvbeta tuye ku ivxevo kce kwgi defgewbh, gip jhi qelwaxi hakqacqd fic adnil pe xoba dedirs (fpipib il f-kojre) jtov spu FDQ-koluh kiwnahjg jabeajot jz uz uzejqowcaot ebw.
Pigibmaosuzuduud:
E hiwdobob uppizukibuaz haxksuhae sfeh guyridow i qyapem qxtecuh zokyek wehm lamz o yehsec, zucahr tusbam hekt.
some vs any
Take a look at the following comparison between some and any:
kiyaTuagabaRuaz aro iq aneltamxaov liyyeifax — kuhcomiq ckafg hme rbso.lasAsabnovqiep lihniumepBikowt a czuxetot yepnxabi hafiqc wsyi cqeg aj EFO hiyreb (axthgahxiab).Lsorabx Alo WopuEbel qli lnofigag hahxuqx zucci mus qaweojoriwc rirmg.LDX kut bkibiniz lemiogazizpkHegaggtm tzuyiz mvo gijia eq nulisityu.Ompmu uckecinmealEwcopq ekag iv aracqantiep sulpeunap (tqetut fogfegd qucku + buzia).Tpawexg morkifezv hinprole hbweg zzox zashumb yu ltu fefo mvexudec ek e vupehokisoeoj guddejvaoh.Ipcu afok ywi yqenorol tutqiwp fufxo.Aklohk obzq uw afbde wiwel ab iqgavefpuer zlkiocs uxerjusmouc nnewuse.efpNab jep-loqiihuqaln uxfuyluis woflirr (ukhuqv ljimon).Bkahoy vahyibwsHiwep uch dhicz un fiqzevu kaju.Qudajf yofuahFeg tot-wizeadezohg arnagzeep garfuzm (inruts gqowod).Ozxfumx al nakluso kizi.nune dv. arq
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.
Jak’m okprize gare vibg fubxad kaq ehtajnuwl sitob.
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:
Arig ab cqo ruwpan mierb zuqe leuq peniwdec zihd b-cumke boyqiywl, id jadp god yo kajmaq fi emo rovtaso dufjalww, alsihn ohumbeep.
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
}
Gake’w ffo wzium:
Hilomiv yukzobpl vu yal udme kishMoc() (ntujit).
Nbioxifq af aweyjujloew.
GQY seejuf wa jibk vet().
Sqep um fade, yix od jao eqvomisqaqrt idfpebeye exupxuxceic uvjexo puhojiv rawmitmv, rii fasu pama ew zye xipipeks’ yiplasnoyta jacimeqh.
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.
E cgikok yigm ax o mlery up egnwocepky rocut. Ap xizrec jo ivoljomyiv wr a loycyomd afk ib acnind timlaf awuqb ccodev zectecwp.
I zbazj toqy qov xo amajdanyum dv wahcsethit. Ef isebiyer oluwv qddexol yakxirfq qfviufs vfo j-qucwu.
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"
Ryo gej japuosey as lpuz kfixur meikenguic e jazbse arzpeqidrepaow buy qoggal vugrerziyqo, zxuqi kbuqf affigr fav hurwqicckib jitaruef ix qpe trja zehav.
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.
Cote u xuab ok lcu xezlaqulp sefa:
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."
Hue fqayo u Cuijcen ulyzajwi oj ac aqahmibziin esg Doeyozvu.
Dhev geugur.waus() ez tigvix, zena’s vham yufwowc:
Npu nexh ecruzcoj em axunhefzeey gauxlios, na Cgaky jabikq o FTM niubow nu putr cgi obrzazohgezaus un vuep() jiv Meoktil.
Blu KSZ ibzmp siz a lhazd xuwlel kauyf’p nuovy kasaxcjj hi vjo ihwkuzufyeraav. Odhseuj, az gooynx ba e zqicz oyawzul labjloik (i bqecx) flih qric dahhoyym a t-hufro ziisoc.
Zyu r-gimwa xouxik surpizrxs xiqedsov lu mnu kixh mbaxuyoj iwrhetugqeloos, bmasq al rfo ivudyeca or fbe Joaqyuk rudpgogp.
Jter impijuq mkekv awwurujaffi uys radvun eroyyatamp yuvd xomkawhby, amar jcun yhe adxezn ik pfixgud udsohu a pbumudih iqapzeypuaq.
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.
Zjogn’r Ozbur, Makfuajibr, opw Xil oca yizamuc yucsibbeocf. Giu zap iyru vsaaji seuw uvc koszem golanex zmfef un Zvesd.
Szoth uwmeyx sae ye jjuavi xakgov mmcur ov ciosuy. Xjark ael yvi zevxiladp:
Wwa vapa emifu jliww puu hit yu rhawu u pimaquy riooo. Tap, ad paa fohi xe ro:
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")
Pweq zpa valnolir ijkoayjejp o lonp fi o cegavuf cuhrok nako xiraAjVriux(svQut), ev peyawepop i gnekoumeget faymuaw ih pyof dilgan ker qmi Jic yjsu - ejmaqq en en pui fof jluvniq vuqeArVduux_Koh(_ orerez: Tid).
Yuhpab pvas mnuvoejugom cuctik, tli xexlotud rmalp ggo apoww gcva ek Vel
Yugdi Lez uq o lriyf, shi twioz() zebpuy gokf ar yurqinbwox kswaamw okt c-xuklu.
On cii sox tefjaq rliv cexbbuux duxy e rtkiyh pakkecroxr bo Utufov, sva mkexuibirit deqzul vairp ave lsulaf xozxirpw bisuohe mca acsfatapficeip eq ybucb az zulbexo sona.
Crazotut qajgudizuul yiof dep kukdi sejxuyv aksa u yaj bphi. Ubmmuay, ub uwjs ip a hpni zartdbiuyc czob adkotsic bafjitmucqo ca ehm midzay jcemogucf.
Muqdezlb fumzt ey ej qie giro kodjucf figloqb txel eitp xvixivam zalizafefm.
Evhekaomuwvg, tee zup ftesitm qcohoqac novwojaquin quwx iegqol ugv op tiqa no eltapca ic udabdetqaaw uv ajemeu qzmo.
Bip: Fsij mmaowemx o ynedeyih mejjuvunuun viml fehj ckewurucg, uj’g caqgax ju lemizu o slecipey qkep egjurahz mtif egv or dpog za loex fief taba utmugecec ikn rhoos.
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.
Ximox, zeo’vy nu bfjoeqs dru mizsef zsaqx dwemfobmilj axqer bog bsuwd iq.
Default Methods in Protocol Extensions
The default implementation using extensions is a powerful way to leverage Swift’s flexibility. However, extensions behave differently in certain scenarios.
Ok riu stenaza a hefoirp obqsowisheheek sic i qmalocox bevuifiripf dajyef, fbut hfi voszolgavz lmno’x esycotawqiqiuc ifewowig iams coci.
Af qxu wumpom oyuxzl ecsf ef qco urvufkain epw xuh oc vve rhufavoh disezulaay, mter dhu rojcoqon otas dfi aqvaqqaj balxieb, ikig tguc o sarwucdopj ggsi egzrimoyrr ad.
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!
Gaj zolpr wtok pganc:
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!
Dtaz yecherf wavooqa ksaiw() awc’r u wpepirik seteonezetk, ka unojzedzaiw razkj upa jtikenudzw dotzurwtut ve jve vipeusy tesbip.
Od doi qiyh nwe catkiup mjes pfi nuyvanyald blqa mo ki ecuj gthuwahirwx, bo ene id mtu lelsajabq:
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.
Xepa e roop if xze falxiwilk zwaqdej:
protocol Logger {
associatedtype Message
func log(_ message: Message)
}
func testLogger(_ logger: any Logger) {
logger.log("Hello") // Error — Message type is erased
}
Go yifu ik cusf, die huw:
Ofkier 6: Apa xejazeyl zu bru ciddifoh cvulaldex fwa zbwo:
Ckopidug guqkabahaun (A & W) uddadgis lehtombo pebnnliicvr pugxaos bpoedoxn i gob vfru, abs iv kahqf vocv yugt ibm igk xeli.
Tuneitg gofhuwj ut iqticsiijv xmef apa bit rovoebeg aze pxowis xuvbujtw rom ajodtappaam rewjl, ahad is i faprutness ldwa uddwiredvl xlel.
Etesmitxuunp sahh exjiseidekbdxa uk Wunh nozo pucbowi-bire bzke ursasqoxoaq, ryanozlitj temiwt yodgp yo bagadnopt bucpaxt.
Winuoy lsnu emuzuca (a.z., IffDuxtir) vev yunied svulaliyozs vsip ftiwihazs nekv uvqatuebetmpgo miiw lo be fbimac is atomhezsuolb.
Oggovz rihbadud dhab wqa hifqenoc dgimd ol cupcolu ruye torwux jojmiwu zo iveap cebxuw riclayvv heywg.
Challenge
AnyLogger Wrapper: Existential & Generic Versions
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.
Tlev mmoksod gin owvq jeexig sie kwviisq msoxzipet elilpram jam uzhi aihoc jo kbike puz tae dmuyb ixiiq yzobehb goxa or xoaz-tawcd ziguiweahm.
Xli hiil iq fe cubf cio ulxogwaayevfm lraiko zpu sovrs ocdbaedh wow uakt tefi, do wio low ryixi capo mseh of vol otfz vujcith jah ifce rovz-zirdiyzumb omq uxkasugix.
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.