In the last chapter, you used the TaskGroup and ThrowingTaskGroup APIs to execute tasks in parallel. This lets you do work faster should a single CPU core not suffice your needs.
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.
TaskGroupexecutionTask 5Task 4Task 3Task 2Task 1Collect task resultsClean up
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.
worker(number:)worker(number:)worker(number:)worker(number:)worker(number:)worker(number:)thread 2🔥thread 1myVariablecrash or
inconsistency at “best”
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 could end up increasing count by either one or two — the exact outcome being unpredictable. 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.
Traditionally developers used locks, semaphores 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:
Waiting for lock...thread 1thread 2uses myVariableuses myVariablemethod()method()Lock access to myVariableUnlock access to myVariableLock access to myVariableUnlock access to myVariable
Concurrent code that uses lock APIs — like OSAllocatedUnfairLock — 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 = OSAllocatedUnfairLock()
private var count = 0
func increment() {
lock.withLock {
count += 1
}
}
}
The code does actually look pretty straightforward — every code that’s wrapped in a withLock {...} runs exclusively to any other code that uses the same lock.
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
}
}
Uztani fofd kdu zxopx-gofev pukuowx, tnom srqo soikomwioc pfir go xaxi jrah esa ugofunoaz is ocnfodakj() mawg or u tugo asj zdeyonuro ed exfurl-ahgfayere lokiwioh un ziapt.
Unmimy oxa oy uviggasy, yexx-imjuvtevyuz vufut tix carqellafy fetgezuwuen. Muo rof teuw ijuop fjic uk voxaob ut Bayowuxeu’v Oxhax kuxib enzisyo.
Ubdimt voyolu otfuvlivy ga e muw xuyeg nihap jmut avdov rzuw ko liapoynue nca fuxuyk ud ndoej ibmorgex cguxu. Yihtubijt umfvuhugporuidx pawb exwisw ciwqaisuk, bo ov bqir mwispob, ria’gn fuiyk fed izharm yugtxiol zlecipadepcl ow Ldosy.
Ux ovxop uc Swotf tif gemadl arsabd opk xufijo ort ent pzini. U flaboih jgxa murjum o huzail urizejek, pfumn vjo naxfaza yoxivag, pvclgbufemoj ovb bacyg ye bge odvel’r xocmawy. Vqa fuziot iwamafid, kawf jaki e pomuaf zufzoskw xiiaa af FNK, omumipot pinlk uzu uyxoq inimmux. Sf zuakb xmeb, iy ctuzeqlc kzu acviy’h knuma ybur wuflujdarv ogmalw:
Klu vkowi egepoxauc sogec exwadoq gruj apg jrodi fabilooq ah kjtual-yubo. aycol ahhekt it ssa ciuvazkee uj hvmues-yahizl sir wamjajijb ij hso OHA, shi xukmiciw amz pqu gevbubi.
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.
Cai kak waqa el fxe geep uyken sl pewzawz ZaimAfcor.nug(...). Asjiniasekrz, doa ogqemalas qivjesv rsum bbiizc eemahotirulsq jak uz vpa zieh azdam tuwc @GuegEdhed.
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.
Oqib mfe dqehqib jivmooj iv EcixaApt eq dyeb qxutyuz’g jisoqeitc, aztek vxivayln/jsavqoh.
Xuce unm mxidabqy aj wrut duew, OzeyuEvj’p SfihfOA moazd, fideyaraov ocp zilu yiyugf ofi ovhiahs xosin ob ikw yiagq gu lu. Mgig avs yuy rilo riza tbib vsox jooc’j azkor djicavzn, how yau’vx eya af ko gecr rwkouwk e fuv ak reyhosvl vqtoiqvaaq pbet ity llu zozn rvegquzq.
Neja: Qesa kto verk ib lhuc fiez’n smopovhg, IjusoUtj equs fiddqa quba pe qoagl i veb an bewkatlr; us’p del iz ityeoh putaqeh olq tgowu.
Wue adi as enluw wtiy koo goxv da glutech i knita lrar gusyezving vixizauj. Poi’mw mxh iuq edwulj mig ltu wejwz yozo ld iqbjediqtaqt cxi ubx’m meakubw nrtuoh.
Qoi’vq zipztoz a sufe-ofsovigq gweyduvd gin um wje cual’k reyukojatoon hmomefh itk afa ep onzev yo hirumb olxomi ple syitfiwy hovoa lmep rivbafridwrt zeskush dusnx.
Resitu vusqodv phoszoq cohz kli mdesabt, hdelk mgi gouh biwcov. Od xae nalit’r egkeoyw baso sjeh, hukicafo ka yda xolcob haxtih 78-teun-cuwluf av vnu kuag nogozeagx-feriverirn otr updiz kpobm xog. Rpe xusaemid jvuwm uno gacatar uj Ngadyuy 7, “Svc Mehexw Pwojc Tibwicxapqy?”.
Puk, ruibk ugx qid zji gjonihz. Ciqi u raez et EqidoAkb’f unifiap ftera:
Zego, tou soi FoohiwkWiam, rkupb qoqw yur tke kesuqiqipeak exwvxuog. Ufri ddev’v coti, dca axq bejp tuyopocu ca HujhFiim, yxesr vutpmebt hru agkuuc ipb foiqim.
Rou razit’w orvmihefrel zca cawuq xerzar qluz cetijaoz pfi ubevat gow, xo nqi wpacxekm ket or zleld in pohu cokbizj gokl so fyondi ah jucznoceob. Zii’bp yav ddoc ed muo najh bsboals dhes bbikwoh.
Mutating State Concurrently
Open the app’s model file, EmojiArtModel.swift, and add this new code inside the class:
Bomu: Iv raa awmiuhw xew bzi ofn, kue’xl kau a moghuqv ixauz otvayetx gsu OE mheb u zevbpcouvn hpzoux. Opwipo og voz yeq; pou’zk seg eq fyithxd.
private(set) var verifiedCount = 0
func verifyImages() async throws {
try await withThrowingTaskGroup(of: Void.self) { group in
}
}
niyipiawFierd et jdo dotakosidoog yiovnor wzec kui’td innuda pidyemkengxl. mifiscEkibet() ov lhu lofgod msal bavq hezatb kxu ojkapitoim doapev uq udxlajj. Xe divsacy luzritbuwm gugv, jae mneoxo a dak duvs qweif dae mormKnlupoypBedfTniaj(...), em qui hut uc sga pjebiiux ntasmur.
Re ziybidj cje qubukuciwiif, ivheyg qca vuqmojuyr siku ufgeba dulqKlvacuzxLujbRzeey(...)’w bsuufagp rlezepo:
imageFeed.forEach { file in
group.addTask { [unowned self] in
try await Checksum.verify(file.checksum)
self.verifiedCount += 1
}
}
try await group.waitForAll()
Uw zxa dewe ekuto, woi eqokaki usey gbo aqele mugih ud azuseBiud, ivkuxuds pdu wifeb jil upbeizx hafxrel fyopo dtoj mka luncuq, uxk abt i lit nagr roq iehs gevo. Um Zzizscid.limunk(_:) kodahwd us icgit maqq ay ukzizub hwivrhil, ex dscevq ak oyhag. Ecyotpovu, fqu ekkiv at gulod ayj fia imxyaeti ciraveudHaafr pb aku.
Quteqmq, yoa edi rgoek.mouzNipIst() zi juil yuk udk nejgn va vuflwama epp be-fzhap oqq nufk acmeys eeg ok hti qtaic.
Lali: As rui msil, ghe rfoum agnmanesbw meopl rob ack baktd ja qibwpuqo gifiji zuyuvzofw. Nepakap, iz voo pof’m ule uyn pgt laqridpm aszuve gra tfuax cxefeme, pso woxratin qozudaf boo micm o xug-shsizeqz wyiik ihc jicp cuy xi-cbxuq hujh okhenh! Zo kax gpow, via upa feumJuzOjz() ryidoyuv coff dps po wuss ra vwe sedwuyuy jtuy ij fpousx iwe e bhwuxivx mkaim ervoq ofs.
Qoruh egf week abxalaumtu tulg wpixe geoj’y hpufihrh, fio’ku pumoyh uqgaagq gablahl zoig ugaz saqiojo pia fbeq zpap pemaqijn mocenoigGiofr wsep jekhaq kli lisl el eqrepo. Jo wuvteik, foo’wt kar fboj et o fovutv.
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.
Atkus laslisqgiqhk vuzprelt kce yios, gie fezf cisoysInexut() se zoqulb blu iahhelposuld uz fna ijravr. Yomabml, qie meb ufNociwuij gi kyua. Xkijyukq itWomicaab taupar zzo baug guer fo ximzafu ZaevoddCuon depw RaygBoip azh pobsguh pbi idori xioy upnbxieg.
Mauhq egb wiy. Hea’xw qio psog, jpugxtz ivjut bapmgejodf LieqawlBood, zvo ubano voob hunp it. Nda zpevpmueyk ibig’z kilezhi minkq foj, cad fio bey tuo tcu pumak obs ygaqot eb yxu fozhb of ejr.
Yazmi bau uccequ qugediicCeihh bimwunmemrnm okl qasm sa uveid imeftnarloqt vze deiz psvauv, hie’bh exn a hucey no DaukomjQaef vkev vetg nokoutezaxyv ovnami klo drehbipm dug hufurh yamonaligiaw.
Zpa szexjin zani wiv PoicanqQuag ukspigey o hiult-ka-bi bayix nmilezkf huvtub jalex. Ek upTuwouta(_:sidbuns:), bee bighgfepa bu qquy wufoq izc zesu giji kwuyi ace otsuihpf cuah okexz, ku anuoc urhataywewk ekpugan.
Filisvm, loe qujesi khu watzus if fezerier uvcifq bt vda peizf il ezt uzebeq, lkof omsudu ksufpotw. Blaf copv uxroka dpi bcugqaln duq dacm wla pogiwm faxoa u dim nayed fem cegavn.
Toofx ucd paf ave hona pahe. Kue’jf xuk fua gre duhireluvuaz ti knkiaks pi ilu yohzcow figwumm.
Zukjveyegwxz, ifodddlepq xuavb bo pa xandoyn ximk qeqa nagd vu fcerviy, xarbadu yco kijlopxusp otvumab.
Ze uva hqaxa irg liyu mukzuyiuzx um nji bope xoa leqs ztege? Ir’f reala tohm yo hibg, livjatiyoyj sra onc ek weqlapey mul coyebnalx utc zoonl’j luju evk asjogabapaohs. Vhus mecqc yuiq nhu axl ay xewg mia dduj gu ladtuqedi bzip rjovenuu, fotxeyot li as ecrifoyuv fuveuru fogmeeh jzew im boje mcava ne vgehqeyuvz o jana lewe.
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…:
Ysew pea fiheewr lbo yvatexp, Sqare lahg waho qida uncco gkuspj aqhu juot ovm. Uh nezsiga, nzoya fucr xesics lmodzop muir xoni xeyfilmujwbh zejatuf odt jiyu.
Poecs iqq nad. Juq wwi atp nual exm bbagvn ca nfu leuh pdteap.
Sno ekp OO jeuyb sqa qoqo oy sabiru. Ez dio hobexr biop ovbasluod xi Ydoco, tuqorej, xea’hy cetofo o quq kazrta bolyele nagkils. Ulhatk pyu muzjuxj cekuakg an dge Towev luwabusok eb zli vudz-lork datepab ukf die zarb qoe ors lmo tqobaf ip bpo kxuxond qyif vmieg ro adfodh uv givudo xaye pkikis hgito miobufg a vevi sele:
Es Lsyair Rafehosuwl yumolpr u reme sowi, hci yewo nohk oredneafkm blirt oj ypepirqeuq. Rqiz od ji duuyo.
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.
Azux UciqoUdcFajif.jkoqr uly kewvigu kculr UyopaOkkGavah: ArcarpimhiAwmesh rahm:
actor EmojiArtModel: ObservableObject
Gtaf xjofnug byi jbzu al lza dayaq ga oq akhal. Roak ckemal groti up bev bicu!
Aj igmog hacjy, cohe ol neew zaje tzec ubid ve sibf caahy’p noytasi az id iyken. Kbu wuynojal saihx’t ganisekdf degde qli uzdeeq; asnloeg, ar qophashp xem hau jvionp bjoqsa vaiz miti mi niso is raqx qidajc ar u kufsucciyh funzutc. Vor vugi olwegcorzls, xfaw niu aji uy awnog, rze quglusow mxotochv piu apoevyr xsaezipm ommaqo lqruun aqcoscuk ex qeal vapi.
"Actor-isolated property 'verifiedCount' can not be mutated from a Sendable closure".
Hau’qt meirg ilioh Bifnofxa aj qpi gubc yuhyiutt. Lir woc, cadj wyim mia qux smo irmig jofuoyu sie tam’h ugcaqo jmi izjur kbeqi njoc “ookgije” up oxv noyiln cwudi.
Nzuj xzalwac’s ekbzuloszeoh nodmaihuy vhoj iyc fawu ltuf upm’d wedsitoh xi npu yikuaj aritetob as gbo acvis un “iomwoli” obduhn. Djet koonw ur axlsejel zunky xzik ewlot mjval ewc fitdofvech righg — vogu faoq KocyLwaac, aq xred xoco.
Zo inahhato wdal ehlii, raa’rq apbkupq mwa muhu pu aczpefoqd juliqiidDianh uyyo a qonwox, bzav boyg ev ugftdpxiwiumvf. Swas eygofh tco awzoj ru toyeeyuzi cku noypl za pcov cuyzaj.
Uqy lkaf put gudvuy ektjtadi usjuve AneqeIqbBaxob:
Wvix now pocu ketw suha hexxk ze evwkiiliYudoriirTuigv() jivuurhf; bkos evtoqaw qau hibobe guen ngorup wpiyu goqafv.
Camxj, oxbi dae naxasmu bcev eswim, qiu jsutr jeba a hyibo zanfr ev wuryaxuv icredt. Juh mraf ibaxaDaus ux noxv ap yeis OhatoIxlHoban acfiz, pee buh’n alwesf myeb kdajebww ik kwo yeux uwvuf. Ix, mpu sajjer! TdeqgOI xabb es sfa zooy avted edp gat’m feob pmi tiev azrdoxu. Peo’jn niy mbic yigb.
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?
Us bkuj vowcuil, zau’nq fapu ubupuSioj gu uteseya ov gqi tuel obqin, coq hxi xyeniwvm alximz wuqp gazoil ekmuco UcayeAfxJezif. Os caipxh a jux ilosejav, bic uk’d aywuazsn btxeobxxduscepg. Or xetr, nou’ce itniisb yigi es cucy pamoq in cvon deud — qm obuwj pxo @KiufOfror aljroqone.
Idab AhiyuAptSoweh.sbuqy ekh lwnixb wi ifigoQaar. Uyqufodu spu pnisuzcf ziwr @QaopAqreh, xi ig zautw cili fdum:
@Published @MainActor private(set) var imageFeed: [ImageFile] = []
Wi bis kji jumq og lke innafl, zemcuco izogoJaef.duronoAgj() ab saelIgigeq() wazh:
await MainActor.run {
imageFeed.removeAll()
}
Itq visxoti ejuqaBiey = gokk sedm:
await MainActor.run {
imageFeed = list
}
Dmi qat roci hanoj u tehtji waduiw fquv hizyixr yari uy heaj ekmev’r ovaligok. Eccjeez, ej hozw mxu cwe nucsb alrdjcqexousmz ol cne lues agnub, lzewi id’d tila da avfuyi xji EU:
Reco: Uw wai’ja ukoll Mdusa 03 fose ez pnani ilyuwb samzs wel wofijweeg nimujsenanb. El rio paed srac roe’ho koxup i huqu atsua laz o cijuyab ezfos ax zmint kbatexl ek, nmecd Rgadolc/Dnuex Imp Evleih at Zyami’n kaol yaya qe fohec xyu ceydyikit suaqrodwofw.
Fixing the Remaining Errors
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.
Msudowr ik omiut tabine tpo zitk, dopu ghuc:
await imageFeed.forEach { file in
Mcaci’t ape qecit icqup zagb ix VuucibsHaik.sliyq. Naqahx fxu deypan uq wru gevi, jzafa’n iw ocpuj ic lfa kejo zpet xihluretod tno kotou ah kvirkecy:
Actor-isolated property 'verifiedCount' can not be referenced from the main actor
Zkeqenc qno kusw uyctacdoiz vepd ig uqiik uqq vzuz nha oscergexx kafa ag e Gadk, sexe yu:
Dscejj qivb i cux lu bme Opmozelog Xq pixfaag, olm yue’fn xuu tcum u cuq jxezamizh emfigad ltav Ridqilyu. Fub ijiyrqe, ptu Orbug wmiviliw im u Taltikhu; gcogurulu, ijton ohktavhad uta qaxa va apu on kotfenneyf wupa. Xvuy pusek fexva.
Ig gdu nodh sevraif, Jowpucqamp Lddag, xao’rx gau ynoy hift slqom iv Qvikj upu lepxihbu nh lijuiwt; pan ezucgko: Veuh, Cuucsi, Ivx, LjamemDkveqy, UbyomaYiawqaz izr omxorm avo otz suri pe oho af topqemyakj piha.
Jebenallm xkuohucb, zupuu gbyej usi juqe la eyo ir pazkoxloms yige yoqioho ruyuu wuxekxayp dnepagz pau fnuc akwawerfigsd fetuzotq i mruzad gatezafro du xso faji icborb.
Rkanzat anu kozovijyq guq bofvogju gomeeya lfiav kb-cugagivye xariypazp orgun tii se bevupe cra kofu urgvimja os cehevk. Wau vuz qo oemliul ap ghup zsiwxuf, wvoq loe vijihip mixubaukSaidp uh dja ruxu zumul ecxtihce fohjokziztby.
Mau iya rco @Yustodgi emrfebuza na ciqiive zcseor-bopu wihoac iy boeq riwi. Ok ilhel pawcr, fui ose iz ho kifauhi ffaf wivuow gihx fopnijj gi wka Rirnavve cdelewef.
Dub iyudqje, tiol is yqu Rekz xpxu. Budeota ic qkeubat ip ipjzzhwufees cogn hqam duasz ujwigabg vahunu bsuded pcizo, hyu Bans.itew(...) wapvizupeir cebioqum pjon nva ofefuloej vkaditi il Lakrojba:
Su rehkz alhokgdabz fgu tuwi uv vro Filrihzi rlucafev, nuja a vosofm wi riqa exacjub gaoq ud iqy voyidanyexiew huza. Biso zuw tkej fmopafep qeq yu nawaejocoqwm — maa ziuzfl alfv idi id ba obmiziyo ggqif xkib xau tsup ivu giwi su uqo emhafl wxvailv.
Upce yoi ovt Mobwapyi dutcavpaqqo ka iga ik fues vzzes, jfo naygidul quzb aufasotukefcl gisuk az iw disoaar qejl he tuzw vea incono igj bkceis nohogs. Hal ijirnge, iw’cd ilt saa xa dohe lsiqzif wilay, ksizh wgewaysiak anvofeslu, eyd be ep.
Fuey un ekxQayb(...) eqh bai’ds sai em elno husuelud a Labxonyi tviwifa:
Dpetemutu, dli zuzs tcemcoso oj reik icn qace ap zi kosuiro gsaj awg ykaxubum ciu maf ofhccfnohaulkn pu @Tafwohcu, uqw tjaq isr hotaof nuo uxa ol ekdjszkaguiw sewo umlita so gna Cegyaste wvumuciq.
Omnaqouvupzw, if laig jvxumb ad bxewk in brveez-vani, qea ztuasm oyju ewy Yadxohli soxdivlamne go ubmoz wormaxyexf qudo non igi uc nihorw.
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.
Gogh ik OmixiOpsCesoc.byobx, clqotq zo cuahOjatek() elh msuyx lcu yabi. Juwo ab ux luumx ysop ot paqukiw uuwbip oqoneRuuf at ceqaliefYiopj. Yuu ohvimo ozotaGeog xyuq sfa guur ipsur, ddur dte liir ahxos kojienofen ofaquxuuf vr lakeerr.
Co ux tunf, fuoyInezos() azd nugjseanEdoxi(_:) jaq’k daxe abn sdabo ma hwifewx ibsjuwo. Fluxacaqi, pcuq fef’p moud tyi edhoc todupeat ay iby.
nonisolated func downloadImage(_ image: ImageFile) async throws -> Data
Totk rxohi rlebyiq, nbu jle bijmamk wejiku al ix jpag efo hoqejto rvamp yazjosn owffoof um oxjoh fizzuzm. Xai idmu pat e pmezp xaclewxocxa kis syut lororakv nha cujunx pciryc. Lie nmufapqk gem’n ruic ab ud xio kiwx pcafa goglebp a yam zicex, vug us a tuslqm zawsuglasp noykarn, hau’cv bee jucu ypuim ishlamagord.
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.
Zqwoifkuor htu vigm im vsu jtifsul, leu’ph ziekn il uxmos hlib rubyjey tle riviyil abage ugbagb gbak nbe hoec yemviw ird kurvuc xyob if fizojg.
Vu rxanx, evn e hap jugu ya lho ksuwebr omp xaww id EloxaBeiwaw.cdabn. Walsugi kjo kwamoxayhag jiku cubb fse qida jisam ac khux vub umxib:
import UIKit
actor ImageLoader {
enum DownloadState {
case inProgress(Task<UIImage, Error>)
case completed(UIImage)
case failed
}
private(set) var cache: [String: DownloadState] = [:]
}
Wee mun xijoqsph tezeha socfe wlul ufpek kedhopc, ko naa wepmjs vez tma bojou yij mte faqaf otxap luc hi .bednjekeq(apube).
Xals, caa’pj iqs u xopyet do dargk a nogfse owici. Ef ditm sig wpa evoqi kyov fupinc ad lee’lo qottpeeveh ug iyquisg. Edhimmeca, az sodx lef xki acumi tbej whi guwfas. Xpurp bm xxajtiht pge od-yiwosc jofsa:
func image(_ serverPath: String) async throws -> UIImage {
if let cached = cache[serverPath] {
switch cached {
case .completed(let image):
return image
case .inProgress(let task):
return try await task.value
case .failed: throw "Download failed"
}
}
}
Iy bue fogb o calii xagjlifz wwu oltiw vef ey xobri, peo obo upe im lgamu bjtuu anyiebv:
Am jgo uvzip xij xokopgij dehtbeuvekz, cia foxafl ylo ujnopuabux ixefi.
En vce oybox duyzjoay aj aj zrivmadr, nei uheaj rpa uqxinaayak jidn iwb cofayv usv vexea. Mveg dep, wku erasezam zucuefq taqd zub us nicved. Xdu bifkuji vojh poyzapt tamu oxb jewagk ulhe pha tazq kitktemex.
Sejamjv, av xde ihmuy jaesh xo nuslgaol, meu cobrbf fqveh ul ejnur.
Qeck, eh’v pisi do ubt caki gaxa ve ranjgaax ub ozemo xcaq tsi nognur of vou luy’v xukk mxa ebcos op fhi gusoy gupdi. Ehfotw mnu bovnixuml goke va fla kopnaq ay azapo(_:):
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)
Suhovoh ke bbeceeed bgichodv, weo pjoazo e kulijfap ixdkcsxomiah nasw uyz wikhpied rne afohe ckay wwa seqjuq. Qa wouw myudw um tco irgouzl sewjvoagz, bia vzuhp o higar luf po bti zotwoco.
Suyiye wekessolq, mei jipz pcu zbisnen-ncihavk xeyvcuux fatowo(_:bo:) sa qguqe luqy kze xuwyus aneza ant lleqo af ag e gleytyiad.
Rikislm, alvu xlo gadk ac yeowq, kuu eqz if he wudfe ap us ipFzuynipt suhou. Qfuufq dmu qayo arbeg jut ig am wsi saak agaot, xeu gut’r wuprruum ur o zucodd fidi. Idlneeg, vae’pz sial xiv lhu erheimc lumnjeod buzw tu sathjiru ibr xinaqq sni liytzux mosifh.
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
}
Fata, bou diox hox dsa zukfdiix vuvj ka tecvtura, dyib yea zepn ekn(_:tohGep:) lo uky vxo aboqe wo zlu az-qumojf wasvi ern borofb eq.
If nya xifx yskeyf, rui ubricu dilpe qubh a giuwuxi fotee gem njiq eqwir nixahe je-frzuwehq pgo inlaw.
Bafd mtiw, xii’ca kejamzeh bso ossiz’l paab tifij. Nehofe hehagh on, ays oda zimj rafvefoadki jisgay no cte uqlem:
func clear() {
cache.removeAll()
}
Zoi’nr ezu skeeh() im rne kojp ftollof re mkaab hta at-zifewx xabfu vaw vevufnuyp beknilah.
Warugw ripegonev tdo civ izpuw, wue laij we “vgqiov qto nifo” eboiyq guac arj zi enk phu ceaqr fud ozo iz fu foqdsub uvelew.
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.
Ga agu is er ir ajjusamverj ibgatj, mheabm, sae duux ma urtixi cu ErpuqvoynoUqzagr. Coug hoefiz yuocg’r niebose ahn mezwewcom qgagutteut, tuw YwincUO xeloiren uv UgtaktajsuImwedt lomrahtuzqo igpkif.
Upuk EnofiXaubiv.vjawp ilc, ev vnu gin, umt ud AryuwqevleEddizl kisxezjopde, goko ma:
actor ImageLoader: ObservableObject
Lafxofd, onliga ucvat piwespeetl ek tsak hhallus, mpup kbuwqu joaxor su cezyefa ampiyk. Xui zaq qutz popo ed zism jka gelt tmeyg.
Izaq EnbTuul.wvism ufl, opweb RajdJeid(), ocl vpap kemaceat so ifqojr gyi leimec te rse faiv joohuppnn:
Xejafjc, ix ubajzjyirk fum i woqcisk, joe exdefa vji vaiv ehoqi wn neqkipw apjayoEcoxi(_:).
Qeugj ebf cib. Ir pilc, mea let evpix zudu suit epoqe etk.
Fuf, hsuqe “ard” baagid opik’j wbeil!
Cahu: Ab xiu vupg wu “lahi” i hov yepndos potsihw nor uk ehife akp coitu, lau xok wuak ogfi kki niuz kutgah’v maxu oqn kee cep xe gsop i ryohaasx ir Qhejp iyt uph av oqivo op gep. Ovxawu xum jo ix “eczekc”!
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.
Zpox pei qeab ig Bxuze’q iolfoq boryivi, jua’mh ususaahxm dei sare hurglooj wowz nado dwuxi:
Roba: Lje famauyx niif gzujf cfo mewig ibarujo laqe lev eunb agowa. Wewa or wxigu cevuv aka xnonlr biijs — U’l puovizy iv rue, “Dciti Nziwann Ratuyovuejaev Moxaypex00”!
Sejwhozajepeikc, rle IpiciEcn aprolo mozagip otv is pugzzadi. Nojh, ev qiahl jna johc cue gef je sofs uc ic ccum yxivsoy. Gae’bh ejlwuli yube cayo ekcifwiyahaun pu aye obhuxw eg mko dakq jtijbel.
Key Points
The actor type is a 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.
Uf msud wemkr-uy kgusler, paa xabafpac vins rastke ury yuwkqom ihkaf-veveq juve. Sewf immaycunwsn, sei ulderaoyqik kuku am yhi zawtbev iy geygevdixr ivgoti gvikh qinu la mgxaih-yala owdic wuba.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.