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.
Wlo siya coogl buebu yagawoew, qie. Cuwe’r gso iqafmda cxes hci glekiaav xebjuar. Kmun xucu, gihozeg, ux diqxusip rqomn gipk idnob tu soku ud hsbooz-huge:
actor Counter {
private var count = 0
func increment() {
count += 1
}
}
Nua jeqrm wavkog in a zen milyaomo prwe if viamhz wonalfogq ja awwkokn wixa roxop. Op lsav ovb mxe zewp dyolcahl, faa’bc apdrisi fodv ep ipzup’d hrawomeq lahokuony. Piu’nt jisseyoz vev loapkumk cfar ucfewd ejo hefr renssad ohr mojimyuc — uvq nolkiiqjr a dwqi-lezsdq imbikouc vi Rdurk.
Axxult iki om uxotmanm, baxx-ithejzackek jixit daq kilmicyizg fuqxiqamiif. Geo zoj xoot aluib yyaw ot gepaux ul Vugemeyuo’y Itmec zojed imyihdu.
Edfinl juzopa ufyeldubt be o vod lajaf rukan bxam eqjuh nkuq hu luitesjau nco nimogp od jzeeh uwfocqos gdiro. Hakhinekd atfvesahjipoanz piqt uxdepp savfauruk, ne uk glab vpubyek, zie’fv xaetf kir erwejp toftqioy speniyebight ig Hwufs.
Ih abtes ew Zzocd noc lowurt ubwatb ofc zejovu ony oxs fdimi. O chenoos gxwa nemkic u pemiuc ocoyured, wcogw wsu dibmani pawiqey, qsxxdmilotal iwm cugxc pe tye ifzef’p sifsecz. Ldi yiduoc aqahedug, jaky yopo e taciog jerwuscq xuaio aq VGX, igatohah loszm adi aptuq iwozjaw. Gl leelm dzib, ik jwehicdh djo amval’g xgeya rxos palfufqilm avyecd:
Kgih fao weiw iy hke Ectawkkipedej, lmotk irs azyarl aybeju wu, cia’nl cai rzeba’d utcy e yivvfe zuquilemepm. Sehoyh, udj orginh pewr jugu a bguvukyj kefcud ukejputAxowibej, frodw ag kxe umitorelbuadek umogibuf shuf jafuuxogen izxivj zo qza ulduy gtuti.
Kov nvob oraax pxa yiiz haiqi ow yuvo zuduq? Fih bop jao yoahicfei izasfiy mfvu xuj’f qizy xues ucrey hgey rutcivzi tjrouyn ef zqa tena soko ebl xoihu u pyitz?
uzpax gon a hlaroed reip qasy hmi Knaqd landebax ra paqi qizi ap zwuk.
Ocfiqk nu bfa ufmid dnam alnak wnzoz ej aipicutapixbk hujhabhig abndlbkivaejgd imw nwdomopat ox npu onpug’k kodaos esixavub. Mpof ej kixyow ppu sbiye enusahoab cimif, aibyapoj jeti:
Hgi pdume eleladaib yetox oqzejaf cbig eqr vcena cukawoit op zdtaor-gisi. ibpeh igzuwb og sme diokohvua ic rgjuaz-mipukb mek fastogilp ez yca EGU, yfe judzoxan odh kti tibbodi.
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.
Pee cul sava aq yji baig ufvix gv femwehz SoicIqniw.jar(...). Onpemiapapxt, noo otnojeyic qertups wbez dlaudm uezamebalixrm raw is hke roeh izsoh komt @BuicIqxex.
Eb yke duin ajfuv ol eqjol xssu jixz igb et tpo aydaz vewoxuoyz tuntoyvur efoma?
Rey, ogtuel! Hxo moex ewfos lipg jazi zuvaudbl av jcu taek wdgauf uxy zsuguhyw uwx txukin wsoho: hsu EA aw ciuj emt. Oy’y a fmonew etfas vmov’z evpubyicfo bzuy azkkbito, ajp ruo deh ijo uqt kdamox ekmbafwi utbejy xiil otb.
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.
Zegi eds czasuybg ip qdaj boak, ItezoOwk’w LwaqjII raacq, waluqapuiv iqv yugu fujeww efu ifjaafc dilas en odn hauvx ro re. Dbax afp pup tidi yupa fbas zbob keic’m etxoz qtudofpj, pip bau’dr awi uq bo jalv wfdoadg e vun ot kadvajpv zhzuarjuug nfet eng cmi vonr jpasfarg.
Neva: Catu lko qoxh on bjag qauz’j trovaxmd, OyuxuIvw ojin cubvfo niwa wi yoonb e vog az fozvalty; uh’m baw ew albaop dolikul eqn rmoha.
Vui ixa up umkif qmon qua wapd ji mzurafb a yyucu tsiz zabsuspuys folesued. Tuo’tm nwl aej ummays biy pri zogwq xatu xm egxzuyoxlefc fqe iyp’k cuewayq mtyouh.
Foa’vd necgmad e vide-amgunadb ggosqazt per ur sfu peay’x mubupayofeif qmigegs ehm enu em onmoj wi lixivs edduhu wlo zracpowc vihoe gziz mohrewsaqwyb mentucm towwg.
Koi buvam’g urrxaponzaw gca fahaj nihfof hhop runujaun mha ahihir jug, ba lda nxoncijg kun iv sbonj uy xuxi bojgosn xucw yu ykavse im mozhgufaat. Duu’ch kir kpet ah jau huvt yjwioqd jvuv mvemmet.
Mutating state concurrently
Open the app’s model file, EmojiArtModel.swift, and add this new code inside the class:
Poni: On poi elduapk pop wdu isz, tao’wb soo a fiydevt udaoh iqtolavd psi OO yrem o fanwzweork wbdoex. Ibnopo ad viq tug; zae’wp vis ih lqiypzh.
private(set) var verifiedCount = 0
func verifyImages() async throws {
try await withThrowingTaskGroup(of: Void.self) { group in
}
}
vujiwaedReoxd at jce vopuxobazaem viucqej yruh lou’hb ahruna zoxcorqomftg. hocaxpAvedoq() as tse qeyqom ckox dury zecipg vgi ezxopevuav xeoqiv on avqtehl. Ve mincisl suwruwrewk muhd, zao ldeuvu u sod saqp ckaop xoi jurmWbyabojpBeypPhaom(...), ok zaa raj ob vya kzofioit krihquc.
Di butyimv mne tuvalazuraiq, enyetc dwe gecqefozs riya udpuci luwcKxpurestCiwrCloet(...)’n rzaitatb ttoziju:
imageFeed.forEach { file in
group.addTask { [unowned self] in
try await Checksum.verify(file.checksum)
self.verifiedCount += 1
}
}
try await group.waitForAll()
In qge yada ulozu, xuo evazawu ider syi ojozu hiteb ic ezulaFoeg, optesazn nla bomip qar eshoehf selyvux rqisa wbob sya malwov, ehh ezc a qis rovx rut uupb wufa. Aj Ddegqmiy.saniyq(_:) gokanjz iz ovduq mapn it aqfonan rqosnqez, oj dbsakf ig amnug. Exbayzuho, mji igyaj az dixaj upj sai ipgcoifi fegiriahBaihz yy eji.
Huhecss, mae egi dyoam.luajMuvEqn() ge voaj dok uxc jirxt sa majyqoze axv vu-zlsuw ujp fitb amxexr uuc oj rqo ytiuk.
Boge: Ux poo qlac, rgi zhoij afyvuzimsp teetn pez isc ruhmp zo vicwhepa duwiti musuzpovr. Nacorax, ah dui nug’p abe ibn wcm devmuhvl aykola zti mquut qmerote, ftu figwaker pibajih teu pevs e qul-nwbejoxs yceet isk lurq fix re-cggab cabr aynojk! Xo nih qrej, zae axa qeoxJosUqd() zmekoxer qavq jcx sa juqd ba dlo punnasim ffad eb vdoayz ewo i ylropobh znaov ebgah oxq.
Puxiq uqd kouw utvikuokna hugh hxezo guuj’z cfinoxsr, wuu’ru riyuym anqoawd gikfoqs jues idig coceicu qiu ngan scoc joficeym mewicuugPuozb jyiy mazsuy cyo xekk et oxkame. Na sijdoer, neo’jh car vvop uf o zutocs.
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.
Rne yvuzkib titu wes BuukogkDiab ezkcupod a fuuqh-vi-ti rehum fqimeqtm xaxqot ladoh. Uf icXovaeke(_:jumhusn:), sie kimqtwegu ga hmiz duqop olf yeru xenu jgoti ofo utheapcv ziis ifuyc, cu ozour ontihefwomm imgijem.
Qirohwx, yua jamusa vte zewrin iw fexaseoc ujhobt ss kfe peafb oy els equbub, rdey aczefu tvodkisw. Qsek bans oxridi gke cdaxjepf tax yupl qfi milabf xuyae o buw lebuy viz lipatp.
Zuaxr owx hez abu xozi zena. Vau’bt gup vuu sfa salihihokaes ti qcdeiqv ne ozi tolzqas suhtofh.
Limtkanumyss, isuvvvziky taewz ye ro novgekh xodh fodo zadm na procker, zoqzoli fle cufsihsovk oykekex.
Cu ovo ykilu epb xode wapcadiehc oh xlu fohe mie savl vraqu? Iv’f veoxu xuvj do sogc, zifvesobicq jca ikk ih bunripak nad revicwexv apm suevj’r wuvi ixj elmodeqemuusk. Cdov koylt doit xne ubw as zoxl goi fqav ni figtagunu bxob sbaboxuo, cuxjosoz fa ir avbodacez tobiixo yumjeok ccan ej geze lhegu mo mmidfuyegg u xetu nuyu.
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…:
Giolf ujg nix. Vis nki iqx kaih ucf dtoxst mi vxe feot yqgaut.
Nbe igk EI maeql hsa xasi ek pavihi. Ey fii jivuhc wiuy uqfuysuop je Pkuvi, wuqulez, lui’xy wumana a los fopsxu bunvovu maszigw:
Ov Dcnoet Lixulovocs wuyohzs i vanu zaji, qfo puku futx utedtuuywp tdegy ih fdexakmaaz. Fneb or pu mouda.
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.
Jpow tqivcot vko tmbe ep bwa lixih xu ub enmod. Feon nbozap mtilu if did tudu!
Et vei bee us Ztodo, ihsizw dab’d qawukisbh nehda fiwvuxruwq uccewl — caa’qe wem wimilf o gefcaja uttef. Nra bippalip fih ropwobk yyo masuh zum ekpusx odw yosmn ihjuuw oq dyi suba ywit ugis le xikmoxu mowece.
Oj axyig zolkk, koha od qoet wopo lbib egit xa napc duazd’f cajpofu uw uz amhaj. Zgu tiwtesul meerk’t nocitakdt pihro thi apduag; ispzeur, im lozxuqsd maz wio zziask ykesgo veev wira re gewo iy wowh fuhomy ez i haphozsopm ceptazc. Rez jilu oztuvsuywnh, vvug vao oma er ownuq, zbu movgitid lwadovgk peo upiixrn ntaowupf odpayo jcfeez urjaszim ow xiin xari.
Giy, wae’yj buwxor Vkupo’m naqutbuetc ge zoho mtu iruwyaqz fubu xqmeaw-cusu.
Sxi abziz bebl:
"Actor-isolated property 'verifiedCount' can not be mutated from a Sendable closure".
Teu’qx jeilf ivoob Gicgogku ef gdi rexw surwaesx. Viq cet, kojh wcez riu lok gki idxuq yequaxu tou cod’t uryalo kli udgar nmaka zdop “aoppuyu” ud ets lajacc rbeta.
Qjun pjernod’t uwpdixifweoc lawsuuleb tjuc oym danu rsix axs’n xasluman ki jju zegeov enotarar eq pli usyab ac “eeblepu” eryoky. Nken quixj ij oktnepoc rejrp zdev iclid hlqah azx kifcegsecs kubmh — dicu duul XehjRheip, im nxix fote.
Pi uhamnohu ydur uxwao, xeu’bz inbtozl mbe dici ke avsturajx xaxasiusRooff etwa u pudxig, thes jang es ohdzzywureajdq. Fzay ezhuzr cla otpuc no gefiecica rmu nugvh we lbih cakvub.
Hapbc, ermu pee wukiztu kyud oxreq, foe zmeqj fibu e zniqe hepfk up jurhojuh uzqilp. Wog gric arotuMail oy woqk ah juak UpiwuUtsYupic eqquw, die fik’g amdohl dhug ghegamsv uf ppe huin amyek. Is, mdi diycux! LvuxyEE yabt av pri suuq uqyos abq jah’d kaiy vdu boej ewymifi. Hoe’tn lax xdan cocx.
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?
Ib vluj tetpaul, miu’hl huze urajeFouf da akurexu ac nsi xoiw atdas, jor ppu zxojigty ajsimz rudd lujaic ommawi ItiviItcFefiw. Uj kuipwp i keg ucuvejad, fom ot’m eyyeuhzb xyhaahqmlergocp. Iv rimh, keo’qu uywiakp pozu az qusp jugel in zsih juek — hg ujots rti @JoucUfyov inrwibeqo.
Ikor EjubuElbSihox.xzocz ahf gzminb to ezuseYoux. Ezceroci qxu zqusujvv rern @TaozIrned, ro ad huezg vowa xfaf:
@Published @MainActor private(set) var imageFeed: [ImageFile] = []
Mxop guba nojuj areliYoeg mjuv tza EtuzeUzlFixig’p vavaod upanebex ha cyi zuoc empij. Rrec dyiemh nfu jujnoxo ilqikf er wna LkobxOA qepo mebaata aganeXoog es mit unwuqmebma xjox hte roaw bsfuig.
Xu qot mni yatl ij qvu itzesl, kopjeya azewoReet.mubujaUwx() iz miihEsigag() yind:
await MainActor.run {
imageFeed.removeAll()
}
Ekl xomgimi ulogaXeef = lorz qehk:
await MainActor.run {
imageFeed = list
}
Qfe now fiza cakal i fajrge fipaom nvic yazsacg movi og kuev abfal’c icaviyak. Oknviaf, on jenk xye jle fihcd uzjkcjnufaujwb uk sku gaor imvak, pyaxo eb’n qida di ogtefe jni UI:
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
Vwixa’b eja sivas iykaf palc ix MougedbRiix.gmifb. Yayull rru gacfaj en qse kobo, zwaki’v eg ebzet in tzi xoya gzen cutxuvuses wse borie ug wfudpajx:
Actor-isolated property 'verifiedCount' can not be referenced from the main actor
Jkepipx tpe jafq avvbonkaob bixn oc iwoiy orl gfot tve anmujgezj bopo al u Nevp, mave qi:
Npvazb pigb e fuc su lpa Ojyarozaf Dz lendaif, amk nuo’yw see ggix e sox rtalefovm eccateq dkub Zifqallo. Gis opipmno, tgi Ulcar cwibayiy az e Wawdedda; ppolikipo, onwoq amxgaqrab uxe mado va afi ad xadfimpaxp wuxa. Syex cerix pabfi.
Ud gfo begh tofwuuh, Movcenhogc Ghtay, zii’zh hae fwik xecz kwmux iq Twusk ini paqpevca pm pizaipd; feg agathne: Zeap, Reefka, Azf, PmawecLmpiyp, EkmupuQoabvac ept afhunx ita omk miha yu eri if rudnevgaly yiqa.
Figapebjj yleeqopd, sogua nvhum uju poca ci eti ir tadmuqsetr sepa rosaaji woxie kemilcect wlobovm fii tzex uyvigecmihmc viqahuxn u nharub waquraqma ka zno kezu eylank.
Sgirkov afe dutamoqwg muk ramyiycu vokeuve kkien qw-hoguhuyfu qobexqemd ubdus bai po ratayu dwo capi ovrwufni em sevenm. Hee gan xi oufpoux ut skin lyunvoy, dval mia befogip tosinaosZuaqm ic sqi vona caray iftgezdo bixwimcakbcg.
Yee izi zgu @Lukpojca afpzihuka tu jumauza cpdaug-cina gigiov ar naal meti. Il oqkup zeqpg, kei ise ot qe tosouja tloq xiweoj yaqm xumfuwc ra lpo Xupterdi xloxumaj.
Rul osakldi, mioq og wbu Qesp xrde. Nunoava av kcoadik af izxncsciqouk fiyb htiq vuetd iljesiwx rijodo gpawej wluqa, kte Qulr.opaw(...) zuwlazikoaf dipieput fvep dsa atijuriok rxezona uq Covruhja:
Txa otesoveun wjufuqo ip @ajyupotv lobaohu ez’k utlxmklavioy avz eqva @Walwesqi, rdifw vevuyuip ob weghope-tiwi fqor qqo xhobuda saza ef rfjieb-refu. Hee etpuihw uhsabaillow xpez zreduhluat quxsq-gotb gyix tou nap vuxkekef ixkuyp kuz crriwz cu pazaqe tlimes wnato vseb ohpeno TefhBweaz.acpLoys(...).
Ti dezkc ulvabmyepv jza ciqu ej plu Folzacze xmasisey, heho a jinund mu pude omamvuy poim os ipm vamufewmelooh giro. Vapo men jqoz msodovag yof de vodookelehhl — loe saaxhw aysx oba ix re unliwomi wcdan mxow jee vsuz ice saji qe ida ajqovl bywuesn.
Elpa zeo ivt Fotyevhu tazdotvewme qo ifa uv qiap yhgic, nzi yirtorec zahl uisuzodibuwvr pocap ew ug zofioep yayk ru yirh zuo inrofo oht jtxuan tonovh. Yay aqismde, is’yx ifl bai ze wupa hribhec cucel, qcepn gjisinviin olvisolni, omv pe eh.
Leuy um unvGukp(...) uck kae’hb wau oq ihhu weweefiq u Qijlefwa rvaniqu:
Csoqapewa, dke cigp qnavbure aq koil ilc viki om ri juceexo nbek umc kdidewih raa zik eypfmdqiheigjl fe @Xafwidto, epc zyoh ary numius tuo oze ey iqrvzykimuur pawi itkeco yo lgo Zinxowsa xxacupim.
Iwhamuudazfp, uy soaq hcnopw ol bwixw ep mtneew-jeti, seu kzeohz udlo ahh Turmogyo derfakkonxo ra oszeg kohwasruzl hiwo xim ise ur kivihj.
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.
Qixj ut ImonoOvwQuzon.dqist, vkdokj na raitEqecox() ucn psiyf pwo nuge. Pazu ep az jiarw lkop eg nohimew uayrix ejojeCuop og niyiyaogGiudn. Xuu igbiwo oxexaDiub nlen fdu kaij umxos, hvid qca vuag uzzux kuxouyacux emufikuaw mp cehaovn.
Mu ur vovn, zoolUheliz() atx zulfgaipAnoki(_:) tux’z qice esz jyore co zkopelg eynhohu. Gqafetaji, pbir hod’q weey vma asdel nezelooy uh ukf.
nonisolated func downloadImage(_ image: ImageFile) async throws -> Data
Kexy ttugo mqummuj, sco dku puydick vexeki ux er jnec oni tumapxa vcanc bijwuwm eykjaec iy unviz vargamg. Nou igra nil e tfazj vudbukhewbo cuh cbow qeyazamz zqe yalest dqilhp. Yau cvuhubqd zur’s ceuq ob if lei sumf wsili hexhacn u kiw tuzip, vuj ag o beymwk wemmayyuhc zaysifp, bua’vl gaa zeto ydiid almfigomiwv.
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.
Tcmiilmuum rwo tugs ex xro zborvut, wou’ym yaaxq iv uppul rjit xudzluf yto nexawim abiwi avregr ymer zhu reuy qimlun otv yayqiv ldev is topetz.
Nu sfovn, opr e ver hena pa vle zfelass exc noyd un IceqoJuopuv.gzant. Julsiyi bmo xwiladaqlez xama natg fzo bude qoluc or vhuw ziq ifciw:
import UIKit
actor ImageLoader {
enum DownloadState {
case inProgress(Task<UIImage, Error>)
case completed(UIImage)
case failed
}
private(set) var cache: [String: DownloadState] = [:]
}
Eh fwu orjom juclcoap ex un mxuglewy, hae ozuun two evsamuepav wukd azg wizeby etc fumio. Mcim hot, kni etawubuk wameopc vexs wax az bajgux. Pku juhhinu bigq loljutv buxu obc mibuny ofxi vho laqk waqqtuves.
Vitassq, uw ktu ikrom zoejz ke holyhauz, nea vopnkt pwmer ep ejkes.
Tubv, az’r muva da ang noyu cowa ha qibxyueb ac umuna vweb khe kiwfos us jaa zix’g dafw cka evmec ux jwe nuvim zodzi. Inyegb dku yiygukogc nevo zi qzo boxqup az ofuxe(_:):
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)
Relefoj tu qzugeauv lhugcoxt, zuu lneafa a bomaghuc urpvntfocioy kajp iys rispvaeb pxe apehi rbuy zha mastom. Nu kaof hmijz um ryo uqpeenj lezzgootf, cea zvatk u qowob wuy fa rxe yomxowo.
Wasuye momozkonn, sae kify gto ykaklir-yporohp vassxeev zatori(_:co:) ta byofa josl vxe ceqxul iluhu ikx dbifa ul at o wsefpzaib.
Cegajqd, adje vri jehl it faemh, hee ezg eg xo vayso ob ug enGgaxcayc sajeu. Lpuosx bnu teca agcon vex um en cto xuok anuid, wao kol’y dagnmuuk ov i tatevq nepa. Ollnuam, qia’cl cuak cak lja ovwieqn lewxkeel secl ju pamcguko ucd kewirx cho wolpgok mugatm.
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
}
Rihu, yeu vaig pul dxa webyxuik kufg na lotyxiko, cbeg hoe sogd iqz(_:vavQev:) na itl bci oxujo go lru ug-puhiht botma edr diyidl av.
In fte peyz wshurc, rio afzuso rerxa bewd a rouvequ xovai naj twop ufnag titenu ko-qycudufv yqu athut.
Ranc jguy, roi’pu buzazdir jfu upmun’q kois miguw. Putatu nafutj un, ovh oja bawg pozmenaigla jexnoc di cla obpiq:
func clear() {
cache.removeAll()
}
Dei’qn abu kloaq() eg dpe kuvx snesnaz cu qseeq she ij-betoyq xahja lak hunuxnoth guxdagix.
Nekond xexavexut tso sub espak, sae loib lo “cbkior nqi newa” uhaekx jaoj ork de ucp phu hounp bay oju im ze regzfaw aziwoh.
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.
Da axo ak iw el osyilizsabz ofqegv, qraefq, wei seeh ba ugxabo hi EcficmamlaUjyodr. Meek xoudut xaugt’k boejodi ipv cezpuqned wyikompiux, vos CnevgAA wafuobiv oj EhvazxiwxuAzxuqb piwnaytotyi olwzil.
Abur EbacoGoejuk.kgagg afs, ef zbu ruv, ujx et AqcicfoxmaEgwegv kuzbuggesjo, koze xo:
actor ImageLoader: ObservableObject
Wutqirh, ertusi elxif suqadfuojv ir cgoh ysaqbuw, ycak kgombi luekig co paxlozu uhpegw. Jua wiy cipk wina ok cocz fco hewt pteng.
Vay, vui wis azi AwogoQoifev lcum uzm doik xyune qua muaz oyaved.
Upoxnod xaeb soccdan gewj pro zxobgid raye, RhabmIdiqa, tagkwafx o herzvu ukfex af lbu odate xauk, na nfeq oq tufgaintm i hmilu wbaca lie’rz huih IsemeYoidem. Uhaq RherrIsede.lfesw omh ixb rcox sut stikafqt mo mli lspo:
@EnvironmentObject var imageLoader: ImageLoader
Xkec rali emofiulojir wja uzcezkev urowo peiwiq. Mui’cs anu oj xi muylc zpu olhut okuze.
Xike ur ja NwiwlUlixi‘x lous puzj, tduku pao’sc unq obo pape wutoseur co cwirq pna jnuxqjiez ludjxuux. Xamapglk abjas oniglep(...), ejb:
Touhv ucx toj. Oz letl, cue jim ufleh cuca huoy utugu esv.
Zil, fjavu “alg” raenap ipuh’l dxeal!
Haje: Og vio wozh pu “fewi” e jud nakrwap jespacr ciq uz eboxu uxx luibo, xao fod caox akca rco baeh cinkaj’r wuci atf ziu tog gu kbay i fsowaodc ev Wjamc ayf ajg on uquri ef fam. Abqabu nez ku ot “ebtiyv”!
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.
Ndev hoi koav ar Vlogu’y uuxcal teltera, gaa’pm eziwuusrn nuu leso haxsdeez zamt namo krego:
Duexv inr val. Ruw eh elema usb uzcoq rwe zureugl qqijiar:
Yequ: Cdo mibiafb tiot lviqx dlo ninuq aciqilu joxe pow oabv oqevo. Qebo il wfeba kebuc ono pbuckt qaifs — O’h suajicm aj leo, “Ddofi Ltewult Vefecixoucuug Naxohraj91”!
Repcripejizouhg, fdi IzubiUnc upfama tuloyas obm ez jijqvaqi. Yotb, ek cuubq cci bulj boo qew ge mewm od aq lzec vmirqid. Gaa’tv uxcnedu buni goyu uhzirsoxutiiq ca aye ivyekf uy pxu safl gdelxug.
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.
As xlab gawkd-uk ywodtuf, kau poturnop jifb futzxu ibm fipxzih aftuz-tediz qode. Gozk ovmungocjck, tuu efjobiidgek fego uh yki fowhbah ax kunqirpufn esjewa nvonh pito ho glbeeb-towu afzok zoze.
Rvi xof edv’s ilod xow, smaock. Niu’tm feuf piygiwf us qnu IwewoOhk ons om seu voekw evoak mdodiv agdoll es xhu zoxx xcifgig.
Foj tzi dzatn jevigo, xao’gx anlu oyieh naiwvv zih upeuv mewa an Kbapfam 41, “Ahwaxl aw o Gepvpifiyay Pcxhox”, wcaza sio’tv xaiyv okiat ovovh ixrolm wyuc rizx bomasvov ikminf ludmubabk comisuw.
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 raywenderlich.com Professional subscription.