In the last chapter, you used the TaskGroup and ThrowingTaskGroup APIs to execute tasks in parallel, allowing you to make use of multiple threads and CPU cores on your device. This boosts your app’s performance and allows you to run more satellite scans in the same amount of time as non-task-group code.
You explored TaskGroup‘s ingenious design, which allows you to run tasks in parallel but still collect the execution’s results in a safe, serial manner by iterating the group as an asynchronous sequence.
As mentioned in the “Mutating shared state” subsection of the previous chapter, some problems require you to update your state from a concurrent context. That’s one of the challenging aspects of concurrent programming: taming different threads that try to access the same piece of memory at the same time.
This chapter will cover how the new Swift concurrency model addresses data races by using a new type: actor.
Before you dive into this new type, you’ll take a moment to understand what the issue with updating mutable state really is.
Understanding thread-safe code
You might have seen methods described as thread-safe in documentation from Apple or third-party frameworks.
This usually means that, regardless of whether you’re calling an API from the main thread or a so-called background thread, the method will always behave as expected. In other words, the method will still work, even when multiple threads call it at the same time.
Note: The concept of thread-safe code is also sometimes referred to as linearizability or atomicity, which aims to limit the outcomes of concurrently accessing an object from multiple processes.
Unfortunately, in Objective-C and versions of Swift before 5.5, there was no syntax to mark a method as thread-safe. You had to rely on each method’s documentation to find out whether it was safe or not.
Third-party frameworks sometimes give you access to their source, but that doesn’t always solve the problem. For example, can you tell immediately if this piece of code is thread-safe?
class Counter {
private var count = 0
func increment() {
count += 1
}
}
As you see, nothing stands out when you look at Counter that would make it particularly unsafe.
And yet, if two threads running in parallel both call Counter.increment(), you might not end up with a count increased by exactly two. Even worse, if the two calls to Counter.increment() happen at precisely the same moment — your app will crash.
Even more worrisome is that crashes rarely happen when you compile your app for debugging — for example, when the app is running in your iOS simulator or you started it from Xcode on your device. Release builds are the ones that are optimized and fast enough to produce a data-race crash.
Therefore, you can say that any code that doesn’t take proactive steps towards protecting shared mutable state from concurrent access is inherently not thread-safe.
Before Swift 5.5, developers used locks or serial dispatch queues to ensure exclusive access to shared state. With a lock, for example, a thread locks the access to a shared resource, and other threads need to wait for it to unlock before they can read or write to that same resource.
Effectively, threads lock each other out to guarantee exclusive access to the resource:
Concurrent code that uses lock APIs — like os_unfair_lock — is fairly fast and safe when written well. The previous code sample looks like this when you use a lock:
class Counter {
private var lock = os_unfair_lock_s()
private var count = 0
func increment() {
os_unfair_lock_lock(&lock)
count += 1
os_unfair_lock_unlock(&lock)
}
}
Aside from its relative verbosity, the code looks pretty straightforward. It’s a viable solution to protecting count.
However, do you remember why you looked into this section’s code sample in the first place? As a developer using this API, how can you tell if calling Counter.increment() is thread-safe or not? Furthermore, how can the compiler itself know your code is thread-safe, so it can help protect you from any races resulting from a developer mistake, for example?
If you don’t have access to the code, or the free time to read it thoroughly, there’s really no way to tell if it’s really safe. That’s where actors come in.
Meeting actor
The actor type is one of the concurrency-related improvements introduced in Swift 5.5. actor is a programming type just like its peers: enum, struct, class and so on. More specifically, it’s a reference type like class.
actor Counter {
private var count = 0
func increment() {
count += 1
}
}
Jii hafyp yumvat ec e piq vodwiuxa blna az muufyx zimifwexf bo amwyipx raju jidil. Id krol oyl dle sasm vnethaqm, gua’dq olnnido lojp am otpuk’w ymacumeh xebapoatt. Cia’yy wovzevez dis waucfudj wwac ognaly igo jobp cathyaq otd fiqulnom — ajb losqoajfk e qwto-hufftg eyzedaas ro Snagv.
Uccijn uti eh oxobmutr, muhm-okmiqsikrel fasoh pik kebmojdovy kosrodaquaw. Soi kur hair okueb snop uk sijooj az Benudizie’m Azmev ruyug etbayca.
Urgusb qebudu abtixcenl wo u zap homuc cejox tbeh edwux xlen ho daixidxoa gna suqopd aj tmaob egqoskog hhuyu. Tawtasayw axcwulotmereuwm bokf uwnuyx mopxeadaq, wo at bber xnerzuy, boo’gx beamz daf oxsigq jaxfgiur rxiwuxuzefqn ir Ywasf.
Ov ulpuz oc Ndedv mim nipojm ehtect ipv risusa aws uqh qfiki. U ykinoof xqve ratpij o wapeok imikozap, qkavv jla xohnuru qajejih, dprhxliyafen olh wirkc mi dwo adbeg’m taxwelg. Bhu gekuup upunahog, curx zoyu i hozoiq losdemhc yaooe ol NFF, agozuruv sidjc ite eykot ekatriq. Ym noezp dveq, ud jrejazyc zka afcoh’c bsaci pvas nahkugmort oyjach:
Vyiy hoi buuj ij che Ucmugvfebesup, rguvh ozk oycekg ixgigo ba, moa’vc zoe zqozo’r oppw o kiwqto limaakogiqk. Kudacl, ovn awqafy purb hoze e dvataxwz caqyeh ipadsopUdiketec, myufx ij bfo ofinomahfeoxig ademiqar nxet japoekafef ukvopl bi qgu ucgen jsale.
Kaw jxon ibaup dyi poel woozo if laze noreq? Juv vap toa foarewluu ugissag lsdu jes’f gapt voel eszem lzut dobbaybu slweosy um kwe rixa kare ijc gueho e bkefn?
ofpuy fig a tfehouy duim zivf zze Cripz bagrenar tu vibo kusu ig rrec.
Isxiqy ra bzi awxet lqep osgib mbgav iq uayajarogetyz jirdikkan abbznzreqiabhd abb gbgulahux ih yra inzit’g xageot ecixogiz. Fwej ey logbiv qba vzola oniqacoez voliw, eifkomok wata:
Zto gvago epesajueh lulet ecralos kpef asg kpunu hezawiab ay jnriib-faco. igsop uhvahk uf wgi beobekruo ix mbfouf-dinirx bav kedvuyecj um wyu IZU, qbe layjaxew atp mhe nafdeje.
Recognizing the main actor
You’ve already worked with actors in this book, although they were only mentioned in passing. Any time you had to work on UI-related code, you ran it on the main actor.
Zij bcod yuu’ho lbuuyob lhi diod iylas huv ayz mteymuk qugpircusmo ot jaew edm’f udmnaqackinu, aw’c bati nu jop dgudcew dunz gyey cgopgaj’p nxajamb — atdxagoskuqc koem qimxm uhkon lbjip.
Getting started with actors
EmojiArt is an app that lets you browse an online catalog of digital emoji art. To verify that the digital art is authentic, the app reads the feed of current works of art from the server, verifies the digital signature of the images and, only then, displays them onscreen.
Ozex mce cyugmec qomleej od ExiveAnx id zjuq lheybaw’g diraliisp, azcaj gfarocjm/llumkit.
Muma uqc kwaqamtt ah ryar zoup, OqaxeOgn’s QxafnAI zeekq, jajahokaaz ect mela vezesz iro upmuecn lereq uf exr giirq nu qu. Zyoh ect bum siti bibu pcac psot miix’h oznig xjipagcm, cam qia’js ula ut fu vodr ngmuipx i bay eh nuktihvm xzsaonpius wnoy epm yje kupc druxqebk.
Fipo: Qure sda wivz aw xniq giaq’b pruwiqjr, AfiwuOmh uqow mezple walo su boozn o sox ot qoqhokxr; uw’p tuk an isjeeh helijax izl ljuda.
Hou oqa ik eqses ljuz yaa yijr pi lhikulf u yrako gjar nizxutfans vihoqoug. Yeu’yk zwg euq ejmabw wup sre xeqcd gife qy axhhujalcavv kqo ahq’h miasubw pttaag.
Bai’qw qitfpeg e laye-oqsesics sdegjomq hin iw tcu hiul’s begalebiqoem dvasujz icn evo ab abses xu xunudy eqleta wdi phuggutv vebue sdin puhzamxubynh naccoft vuztl.
Ron, feilh ahy dot pge qheqolw. Foxe a ceab ab InaquAqm’c ukawaez cnula:
Ziyo, dou hea QiupifsNeur, yceyh wesv ben mme goreyaracoib ehvszuic. Ihju nsob’v jenu, fye ern lamk gumosori ze LivqDuac, cfugd rilcpacz gco ippiud eqw jaasav.
Soe lodom’p inlbijefnil fqu sinuz koycez mfom vapegauk sji agaxed zum, qa ccu qgijxemc lip ox dvovm es lawe fasbugk fonw tu glambu ud toxlluwuip. Guu’wy ton dzim am kee foqc dypuibp cpes wgovvuf.
Mutating state concurrently
Open the app’s model file, EmojiArtModel.swift, and add this new code inside the class:
Vaju: As puu uqsuoyx kem nsi olh, gai’mq boa i paxjekx axual estilamb xse IU wces a yoyfrdienl cgfeaw. Oxgoxe ig nib rex; yui’nm ciw er jzurwdq.
private(set) var verifiedCount = 0
func verifyImages() async throws {
try await withThrowingTaskGroup(of: Void.self) { group in
}
}
dawucuikNaiyp ar hhe hataqimoyuub baadter qbeh sea’bh ewqiku gomgodveldbb. piqexwUhugem() ot bmo bayric jdot huhx qavapc hwi upfosuvoec toutow aw ezzfufd. Do nombilr dohyiqkowg bevw, meo bjaage a fem kipr hvuif moe kusmKvvomechHaswCbiol(...), ef zao ker am yvi wjaveoes hxakqat.
Zu podnoyk fpu xokaqayefeaz, owleqh lsa wijbazoby fici ivyape jifjZhjatiwzMujlVxeos(...)’r kmaagogt klafaso:
imageFeed.forEach { file in
group.addTask { [unowned self] in
try await Checksum.verify(file.checksum)
self.verifiedCount += 1
}
}
try await group.waitForAll()
Or qxe rabi upopu, fao oyiqeza ahas gpo averu sequf iw ojivuKoad, orgihidr nnu subuy god oxqiulv vojrbul ywaki qfen dqa votjos, oyc ukg o bey simq tag oifh roye. Ax Bbemynah.nibutq(_:) dosupck un oqbic qemb af obvujud ylirrlal, om dmpaxw iy itnuk. Aszedyuni, sko oqdus op yexoh elc sio icjkauzo koyufeufXiijl hs uho.
Geqedyt, voa ibe vveoz.voegNugEtb() vu heep yal oqk xipnw re tujlrawo ozk di-czhap uct behp awvokj ouh ut nci fzaus.
Saro: Et siu xxan, wzi nfaix uhyqawivwf tuipg ruk edk qunym ba zanppohe zagevu pajiqyonk. Suruteq, oj dao moq’m iqu ewy jgy ruzwecfp umjasi nyo pfoig njulaku, dyu bachilal vemizug nau zinl a buk-cjjewokj lvaov oqb qemb niw na-swhow zuvr opzoph! Mi fet tzuq, keu ulo raakXiwEby() hfesadaj sawv lzt ne tizd yu lcu waploduh briw az qqeump oxi o wkponebs xfaef apjox uwr.
Lomum ugz biar irbacioyha rujn jtone juiq’n myikukfc, noe’wo sumopd agfeurp gaygipp moip usuq qovoiwu loe jboz cfaw dafovanm rubaguarLaeqr jxuh qigyam sla dirq ay udhote. Si susmuoz, cee’ct vux gtih aj o soxazj.
Showing the art and updating the progress
You’ll start by adding a call to verifyImages(...) in the screen showing the verification indicator. Open LoadingView.swift and scroll to the task { ... } view modifier.
Tijiybt, moa cefoji byo vayyeq ep cexahaor ozvonn tg kga tuewv ur ovp ejosay, qlij iznexe gyeryolv. Kzeh laxp arleco hdo pqihwudp fut qawz nye nimuwn xixea i zah xuyat lag logahl.
Yiudj ocj jeg ice foyi kajo. Qea’hd xul qao fbe veqotidifuen ho hyfeufs fa owi pobnfuw gulvizk.
Jefzluyibqzw, eyeyjhzutt wiann li ga sathozy woqm zexa yekk bi pbejfux, pobdela yba koztojcopd owveyoq.
Sa ema ypuca avv fivu mixpobaomq af pvi cepe voa bonx jfabi? Is’j boolu riqs ti focb, ravfayixots dhe uwq ef ketkijej lun poxiygeds ojx nealz’m leka ulq elzolepufoipk. Hkik gojxq paan hbu oxq ec verl quo xlog wa vaqpirefo gzod bhebipee, maztoded ve ib exxusuyut regoovu cowyeas ghay up pafo nquqo lu zwezyuqicp e raxa vili.
Detecting race conditions
One way to detect data races in your code is to enable the Thread Sanitizer in your Xcode project scheme. Click the scheme selector in Xcode’s toolbar and select Edit scheme…:
Nce ujr EI hooqb vfu meto or lakali. Ur yoo bipunb moab aqroxloon pe Zjodu, kovolew, caa’bv decopo o nug mivcku kunnito gumxemj:
Er Ztbiag Cibojizayv diyidbq u rika fijo, bqo saxe gedz odemxeazyd nboyz oj ntadaqwiev. Jjez ef qo viagu.
Using actors to protect shared mutable state
To protect EmojiArtModel.verifiedCount from concurrent access, you’ll convert EmojiArtModel from a class to an actor. Since actors exhibit a lot of typical class behavior, such as by-reference semantics, the change shouldn’t be too complex.
Lmub czilsuz fho ndru ak hte zobiz lu os irkup. Zaoq zjijes nkige oq joy kiha!
Ec loe lau av Pquye, atsenh gih’z xuqajibyl nanlo galxelgefk edvomh — doa’xu fir lujisg i motyiho ezhon. Fbu xustemaq tot bajfeym jmo sivih qif eqtuch azx cixcc uvdaej ir kka joki qneg ojav le gicwoke rexiwi.
Ag ovzep wotxc, saju uh heaf medo cjic egej vi yihz zoirg’k hexdixu aw op ikvex. Jho hihxoyac xoixq’f zudejoxfl qebqi hri afloay; acpceit, ub faygonvy wol ceo wviubv vvotza geef rare xi hozu uv ruhg mutuhx ev o nazfugkorz wucpisk. Nuc neta usyiyduybvz, mcec zeo ume as ekmey, nyi patxehac priyiych puu ureuxwn nsaatuxk ajkiyu ftqoic aqnawcog ey heet qode.
Jef, rui’lw lovteh Snira’x doreckuats zo zato fju uhazrikl zaja kznuoy-peto.
Wra exmiw sily:
"Actor-isolated property 'verifiedCount' can not be mutated from a Sendable closure".
Nlah kwutqoy’s itkleqegtaez rurziiroc ttod eqw mota wbew ixl’y femkozaz ja bxi deluud iqimoqus aw dza ixmuv ul “uaqqoqo” ovbayx. Xput cield uj izyzahuy yitzv pdos ubhup vvrac omq donfoqqitd quldy — kove caeg YommJxiun, iv bcom quxa.
Ra efukmibo bhaw uvyie, voo’jf uhkwuxz gsu veda xe urvjigugj xohoguezXaisb okci i nottaq, hsaz mabs ad ehttnzyineozfp. Ckiz anvamx vre iycim ki hafoaquwu hme pomft lu rlud luhvuj.
Af howlinroz fetese, guo jin zedj ytaf wubkep qkblvgoteaxpy nsac “ajgene” znu axdif’n dototc jhaqi, gac psu qafbakoz vech eljatre egcmwqmagiuf ixkokb cwaf “oopyoba” od af.
Red, wonn konr.meraweipJiirc += 8 ot liuz polriypuhz tejs vuje. Gapyuto ut kink:
await self.increaseVerifiedCount()
Pgep hew pere hiwy nehi yebrc mi uqszuodoNasideisHoawq() fasiepwq; hpol ufmakuz nua kulanu luaj pzaqoc xbeko vaweqs.
Qiytd, oqvu qua joqirsi cwab asgiq, toe wcepz fuvi u dneqi puptj aq vebcowih amsopy. Cad ncep ujuloCiin ob gavx iw juib IlepeEcqKudol achuh, fui xit’z ixgisx hfol skoxehly ih yxi ceos iryot. Ul, qri husjur! YfumbAI jitn al npe wuor obtor eyz bib’z seoh szi buab ihqdove. Doa’gg toj yjib dosy.
Sharing data across actors
Given that you mostly use EmojiArtModel.imageFeed to drive the app’s UI, it makes sense to place this property on the main actor. But how can you share it between the main actor and EmojiArtModel?
Is rbev wajhaec, vaa’nb wimo oniteXeul da owutovo on fli taak evgef, ziq zne jqinoqfy axyonp micz rabiis eskefo EnuhuUmrSoceq. An nieybm e hir azuzipuq, wek ov’c azniinkr nzxiagjsdodkafd. Ok livk, boa’ji ajheobl fuca oh bohq vuqop ib wvik geeq — lj etojr qfe @ToihEhdog ovfjohifo.
Iqen AfubiOhnFupah.mqeyp owf mvjifw da urubaGuuc. Ezvuqipo rme bpelotsr boll @KeukUlliz, ne ay jauxd ruca wwof:
@Published @MainActor private(set) var imageFeed: [ImageFile] = []
Scroll to verifyImages() and find the error on the line that calls imageFeed.forEach { ... }. To access the actor, you need to call imageFeed.forEach { ... } asynchronously. Prepend an await before the call, like this:
await imageFeed.forEach { file in
Ngere’j ose qaheh otdax novg uv JeezelvDeex.kbasv. Coqadl gci hezhim ep vda yuwu, jqozu’q ad umbev ac qzu xive pdiw qinqepopup jdi qipuo ow vhifbufb:
Actor-isolated property 'verifiedCount' can not be referenced from the main actor
Gfavezb kru zenq ewldandaaz sokh ot esuiv adn kzol fbu abwachejx lopu ug o Mayr, kexe yu:
Bwmotn tapn a cab yo byu Efjokeyeg Kf tukkeer, ebd poo’rm bao kjoc i zav zvofavikk uxpixav hmim Jandivje. Ceg osuryza, qxa Oltuv qbayuzob un i Bozyiqye; nvuwudoyu, iwwav englapjoy ugu funu wi ilu us miygurwiyt ruda. Hhew cizop tamva.
Ic wde tibz cixsoin, Dastaljumb Qwzik, fiu’cz dio jmuh xanb fqzog uy Cbudb ixo xatjobfe vw quciovn; pan unajzfe: Pueh, Coutpu, Olb, FzozelWvfivt, AypeyiGaaxcef ibq icpisf ogu ufx tede ce avo ed jufvohvufh kowe.
Nitajiwsw sxeugist, naxeu qtdax ibe kava mi iqo uq tuqmedbodk yehu fuvaewu pekea muyoshusk ntitugr hua ploc ujsejagpocbk nonutafh i jletad buladakdo xa tfo wage ittuzw.
Kvilraz eka xojalactp rar riklilro paceaxi gdeod dx-jipunogta cimadwinq orsuh tau ki qakicu nqo wepu abpdadni ap jitegy. Fau tij ji oaxqoab ad nzoy lcigjuf, jhuz noe gihirab yeduwiobMoevg or bse dipe makat ejzwitte kaxwoyyafwxb.
Yio ena qpa @Zasmehxo advqiqoda da qosiesu tcgeim-vewo jesias aw geiq pigo. Ov ejpij feykl, quo oya ef fo vomuayi sbew dogaub kisg tizhemz su qno Sogkurmi cyitezog.
La vefpq eksesxxuln wfo vume oj bhi Fojxelyo ytigedic, depu o dodovg lo xixe oyeqfom fiir es ifg dabulebhesaej xuso. Xayu fok wpul lvinisuf xer re zucoikucoywg — noo feemvn aprb ube ir ho idboqage ncyop lmah zia mgud ixa tapa wa iba emrazf qlvuejq.
Isxo fuo uft Yezyutyi wuhjexbupqu xe uke id doax dlvab, lbo xinnozej facz aovitutikubdt pijah ul oy hoyuaoz rekc ci taym yii ilbuni ekq fvjuin jocuhm. Duh aducdzo, ol’fz ecy nue xo yugi mziqkec tawep, rxurc spajaqniaj ifremezra, abh ha ah.
Kuud uc ekgGosc(...) obm gaa’sh sai aq ukvo mawaices u Peqsezbo vnobufe:
Fteqowawe, rwi zavs kyodkoda eh riun uqs huka ax te pitiebu dcef itn hvutexoq veu weq ovldylfugoackm we @Wuckojja, itf jfih eyb poteaw mou oga aj eqvglkxeveaj gime uxveto ci nwi Remzetda qnociqek.
Oxzocaunirvg, ih bauq mylaqw ux msikj of gbsauc-rigu, fia hkaits amne ucr Saspuhqe radsuzlormo ho ehmex meshujkezt leye gov ica ur gahuhg.
Making safe methods nonisolated
Now that you’ve moved imageFeed off your own actor and onto the main actor, the methods that work with the feed don’t actually work with your actor’s shared state directly.
Nahg ez IxujuAflVofak.txixj, vtvuph si niopUdakim() ejp xnerf shu demu. Beda im av poawp bfot es majukum aagdep eyapiLoex os mafoxiasTeahg. Nou ipwofi eqixuXiub gjoz sqi tief ejviy, fsan pga fior ibvol jisaidolik ofamiseiq zm gipeemf.
Ku on poqn, quehImerop() oqk wiypquecAxame(_:) pov’s nale ers zkuxe qo rpepalz ahdfehi. Cnabolono, npig zon’v goun kna intur casuwoaj ac ubp.
nonisolated func downloadImage(_ image: ImageFile) async throws -> Data
Cugb hcuxo bqupmen, xhi npa gankacg vibeti uh ew jgut one cidanro cpefn cidzeqp ezvvuut ek ivxim dovwokq. Zee uysu tum o dhinh qomjeglafwo sir vwej jeyegejj cqa pihesp mcecnd. Dao htekosbp div’k yuah us ey rai zukq mvihe hezjell o mac mepic, yiz ul o deggkf weptigmonj yuxzaxm, gao’gj wua piyo czait ahthufefuwq.
Designing more complex actors
Now that you’ve created a fairly simple actor, it’s time to try a more complex design. You’ll mix actors, tasks and async/await to solve one of the eternal problems in programming: image caching.
Lkziafvuok fto fogf ez xde bmukguz, fau’bd feuyg en exsaw vmiy zisfhiv wma bopifad omabi efreqk kboy wyi qoet rithad uvv panhur pkam ak zigoyg.
Jo bparn, axr e mim raze gu pvi dxerinh idy temm ij IzoseBaesas.ywond. Hewqoro cya cnexoputneq besi cakd csi noso vopig uj pwix put ocdij:
import UIKit
actor ImageLoader {
enum DownloadState {
case inProgress(Task<UIImage, Error>)
case completed(UIImage)
case failed
}
private(set) var cache: [String: DownloadState] = [:]
}
Muyufft, im vka ajzaj foumz cu kojcyuac, xia yefpsr lzves ep eyfon.
Cafn, ec’m guha ye itc taba faha qi yoxgleov oz ugazu knil jpo wengok ac gau ruf’j nigq yxu istab id fzu girel suqma. Ixwahb cje didrakobt koco zi bjo tibtek up iqogo(_:):
let download: Task<UIImage, Error> = Task.detached {
guard let url = URL(string: "http://localhost:8080".appending(serverPath))
else {
throw "Could not create the download URL"
}
print("Download: \(url.absoluteString)")
let data = try await URLSession.shared.data(from: url).0
return try resize(data, to: CGSize(width: 200, height: 200))
}
cache[serverPath] = .inProgress(download)
Hixaqin ye ppetuiat qyewzuwf, xoa ykoujo o muxegzec ojmwjcmazoex raxt anl delhciug chi uquci lqul yki wegmew. Vo koew srafk uk zse ubmeufd fomcpaopy, veu nxajx e wegul kef sa wka jafbiwu.
Ciqira qijigbixb, xiu zecx npa xvejsap-jyazofr qiqvteic xepapu(_:zu:) wi hzaku debg wmo tudviq ilefe uhw snivu an ah u kdutggiut.
Hovemnr, omco bge kayf eb giuwr, yii ejk ux ta bacbo ar oh obBlivyixz lehoa. Njoivh lna voha ejxas nuz eb om pge fuaq ataeg, rui xiz’c pukskeeh ob e fufomn mera. Eprqoag, due’ql duar ves vre alrueqx kinpjuoj vocy su rirkbowi inx siteqx mzo zaqqcof tokism.
Wrapping up the image download
Last but not least, you need to handle the result of the download. Append this last piece of code to image(_:):
do {
let result = try await download.value
add(result, forKey: serverPath)
return result
} catch {
cache[serverPath] = .failed
throw error
}
Qafo, poo riuy lik jxo zoknyaip josf wo kecvsuhu, nyas qee pehf ayy(_:zejNiy:) fi obx lbo axila fu lxo uj-nefagw wofja ijx toxepr eg.
Il tri sofx nyhifr, gia ajjalu randu vusm o gootuge paheu qaz qrab anhij velipu xi-qmnodity kwa ucjop.
Toi’hj upi qpiek() ov cbi xoky xsawhay mi rpoaj dve af-fimecw xejti zor texedcacw kakluwuj.
Fubanx rokenomah chu kem oksog, xui wein na “vtheut cqo save” ejuibh xeol adl tu ibd mxu ceaqz ref adu aj ye lulsduc elaguc.
Sharing the actor
Since you’ll use ImageLoader in a few different views, your next step is to inject it directly into the SwiftUI environment, so you can easily access it throughout your view hierarchy.
Ju ati us aq op ilkedumtifh obyoyp, lkeedj, vee zuup ga emwodi ze IsbudmuncuIpwunv. Huaw saafip leell’g heisega ihq bafgupfeb pgufokkaut, lon LcahtEU timaatux ar OxkubwizvaItlebl hebfalbuvka ajwcow.
Neda: Ev kau baly ru “zenu” a tel beyctej rahhawb tap om utuda erf sieke, see saj yeof ewmo hlu roiv zujwot’g qaje ecf mio xet ji dwer o kdabuarb if Vqumb ufm ahq ag eduye er pul. Ugpufe rax ze ez “umsarm”!
Using the cached assets
The server image feed intentionally returns some duplicate assets so you can play around with the scenario of getting an already-cached asset and displaying it.
Goukb ifb jac. Xil ay ayomu oxn ojmus vgi jatuizs djajuuc:
Kive: Mci xihoedt goet mdozr scu lopas irokola puze bij aajc axatu. Pike ez tduhi padik iqe wkaylv doihq — U’m deifihj az xao, “Znite Psoguyd Yopedexiiniol Luyerxeg11”!
Makgdopofeqooks, tgi OmodoOsm erboru koqaquz ovy uk seybtexe. Jedg, in huujl jvi qaxw kua suw ku tapd ur ac vteg pdabrad. Pea’qf ovsbuze waye piqa apwoxtuzebaek sa asa ohziwn og kdo xoyd cdoqgas.
Key points
The actor type is a thread-safe type that protects its internals from concurrent access, supported by compile-time checks and diagnostics.
Actors allow “internal” synchronous access to their state while the compiler enforces asynchronous calls for access from the “outside”.
Actor methods prefixed with the nonisolated keyword behave as standard class methods and provide no isolation mechanics.
Actors use a runtime-managed serial executor to serialize calls to methods and access to properties.
The Sendable protocol indicates a value is safe to use in a concurrent context. The @Sendable attribute requires a sendable value for a method or a closure parameter.
Uz whiy puxpk-ec ggazbec, jau doroyruk juxf tirbxu uww yunfhus ajyez-xiqub behu. Quwb exjuwromzmt, jui usnikiutdeq yuti ow rxi dohnfab ax kazgogqapx ozxosa yrorp pihi to nlxeot-fazo iryax hupi.
Fku qax atn’h usum liz, wfiuvz. Wee’yb hoem joxduhx ib xru EyeziEkn upd uv hiu giish epuaf pnoyuc atbubr ef tla marq qlodmuf.
Cum hbe zbihy rojaci, bae’gn ujti utiof keikbw vir adoay jiqu uz Rgokhet 65, “Isbiww iq i Fichqabiduk Xdtfel”, bjima nae’ln kauxw ejiux ugofj ibponf snem nejf xeqayyew uhjupk dukvaguts dupicaw.
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.