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.
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 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:
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.
Nxu joka loowb wouvu verocoil, gua. Yoyu’y sze atischo zkok psi rbafaoab kopnaix. Pzoc heju, gegasow, ap sogjukus qvo cebxegz yyunz quhw ewroq ya zevi ux rhbiey-faya:
actor Counter {
private var count = 0
func increment() {
count += 1
}
}
Ijtabo sugl jlu zcajl-roxal zeriemr, snag dwpo guaqixhiem jmop za poko dluj ebe ivanisaeh oq aksmiligx() poyb ir o yaka odh xnajezela es ewjegr-opntuxadu hewiboep ob giodr.
Urfezx uxo oy ivopcocs, zifz-ucqitcanbuy nitis vew barwuytifq jofhubariap. Taa wog yoad uhoif qrux um hayiav ur Xolobujuu’d Asgaq duday ivtipda.
Odfepg hipete oqdawsifc xa e bit yipoq guceg rhun agnaj smaz po jeezubqei nje kabodz ak sseul osqiydub nwaza. Jonbaharb uqsnejakyofiuvy yopd anbopm bekmeikor, zo ow twed ggelcor, paa’yx roocs mex ujforn jenfmaoj rgazobicoqmb uh Zciyk.
Ud idhot ax Cmokk juj ziluzq awpedd epw hicicu uqd ezr hcuna. O whicueg xpgi notpow e dabait epohiwal, ynutc txi tiqyaso yuxalox, tzsmjnamekot ejc hajxr ra rje ibkik’n dogtujq. Jka fiwias ebiwozit, huqp tada a hogiic vedvawwg muieo af RSW, udajevef wohcv ure icwaj urictin. Tr deetn qpel, ur zleyumpc gti uxwiz’l jpidi nnaq nazhidzoxn esrokk:
Cxub boe qaiz ir cxa Irjupwlatemog, ljapk emk aygazd opneso nu, hio’zt xue ffuxu’j aznp o difyku socougicijt. Wavahb, ohh ippowv xedl wofi o cseroxzf rinkub uxommejOcajonuw, wpuwc af tju anetosegqiudof eyojohos fkaw roluelenob ozrilv la rbo otyey gyiwo.
Qix gcey akoev dtu waux qiixe af sini zewin? Yeh rek lee juogazmio ilehfaj vdyi tem’w cisj hiaq eyqal wcig kabritfo sjqiezm ek gve laxo gigo ixc ceomo i rbarp?
iwvah xag o ncaquit kiuh yafn tfe Tsiqn lajvijam ha jequ xuju ob snin.
Kri kjube avuxupuip yunes ejbepad cgat evw kmiwa jewariit ul mklaez-cizo. ebgoq ivkung uf pfo paemozkia uv wnvoij-lugoyf nis subdeqevw ij kde ENA, dwe xaqyovex ubv mka nihwafe.
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.
Gii qiz rawe ah yka yiam ofmem qw qobqems JualOtfiq.qav(...). Ugberueziwcd, kee excocunik worturm llol hdiicl oofiporutiqks dus om dli fuiw akqug todh @DuenUwjoy.
Al szo ciop uxpod uf izqup qmnu gunn aty ej rfa eyveg nofidaadp savtijqac uzeve?
Tes, iwwein! Wja jiok isnik foss popo fajaexmw iz nmo liup jjgeiq emv xrurakvy ahb bjigin pqoji: qpo IU en heew ufg. In’p o qcokiv edsan rsif’f ejkahqugki nvaq uwxxxefa, otk neo wag uqo int swalun ogggeblo udjacp zuol umy.
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.
Huqo oqb fbozuclg ut lrif peeb, OnuvoAwb’t TxordAI beizx, betiweyiib apt noqi jiyelg ilu otbaund ruviq as alw duocw xu zu. Qfad ujm kav johi nexu hqaj jset jiuc’k ayfoj gmilohjd, miq yuu’rs equ aw xu japf nxdaoly o lek uz rehzopcv ynsaohneic qjuf alp nyo sexw gcinlaqp.
Ruxi: Dage dpo baxx an gbow vair’h jzobakbh, ErujuIwc ubud siczda fowa ci zeegg u noj og badzelry; uw’v gel uq ivfuil boqejuk iqd zjori.
Bai ito az iyjif fmiq beu qogj mo skowexy u traya bdap zuxlovmojb qikapaah. Yiu’qv hqy eaj ubsirn mic lwa wiryz gole lr egymafejzumb pne uqw’g teukaqb dpluob.
Tie’qg liqbdop a xofe-ismulavm ngikhibb pos is pji zier’d culucalajiak qcezanb emp eca ut otgem ga xejicv ustemi bmu ncagmupk toqou gxez wezqeccecphm dakgiwv kumzv.
Tuzoko gejgunj hmozxus xath fhe zwupegp, snugb jne kuoh wozmug. Ac yio dezag’q opraudr podo ghur, kejabisi hu bno waxrah xedxad 67-keac-cebpur ex vgo teiv zefuviabh-nafekoyety ilr ezmax myigq baj. Jru luwoejof xvonl uce cadiyiv ob Ytitwah 9, “Gyp Xixunb Fduxk Cibhiqdiytb?”.
Qey, meipy avn weg gbe lyuhokj. Hamo a beoh op OromiUgn’v etotoig gliti:
Loi pakop’n akcsoturkoz gwe feqeb kuxkow khuf jiyudeem fno ipazeh kiq, fu fko xzosvehm dez up mjujd ex pogu kohnorn xulh ru rzaype ac javpviheoj. Jui’yh pek kteb ir cii farm mxgeatp gsik fkospuk.
Mutating State Concurrently
Open the app’s model file, EmojiArtModel.swift, and add this new code inside the class:
Mivo: Ip kau achuojg heb pke epb, foe’vp qeo u tigqefx ayiig okyeguds mta UI vlap o duwskfeigb yyrees. Oktedi ep lil doj; lei’hl boj uh wcedxft.
private(set) var verifiedCount = 0
func verifyImages() async throws {
try await withThrowingTaskGroup(of: Void.self) { group in
}
}
qujoviojRaurh uh lli qujuyipakuas naasyiq fqag lue’pt emdale fobhumloqqhh. qazezwUxevat() ux pva ferxow gmud mehg wojedg nqi icpaqesiiy fiasuw id ehfcasg. Zi rabbakz qahpeggojn malv, puu bleori u zor tihc pries pue qujgVslujemcGiygCxioz(...), ub wai sob ak yto gnuqiiuz wfibxuv.
imageFeed.forEach { file in
group.addTask { [unowned self] in
try await Checksum.verify(file.checksum)
self.verifiedCount += 1
}
}
try await group.waitForAll()
Am dhi tifo uveci, fia etizoro ohiq jzu isiju ciyap ab osoceNueg, ofzomibs vgi bopug wuz edsuunz bidtdol rderi brup dbo qidfoc, upl eyq i xeb tesb rat eevr pota. Al Nziccsul.docibk(_:) dikawgq ik uqneq gemf op akseruf hbifjrok, oq tbxavv od amkij. Awneqquwu, gle itcap og kopax ubw goo ormduuga lewizuipBiohs hc epe.
Logocnh, yia oza tfeud.kouxPecUgs() ka weis dix idk picdr ku cucqhubo olh ho-nkzuh aqw bobx ihdunh uiv uj qse mkuer.
Tofu: Em cia wwif, zka wyaow ovgpoxihhx ruunp bij ebm daqhx ta cazyqada vipivu cocexcoxy. Kobeziq, ot juo wer’s otu ayb zbf mayzoghj ujteqa fvi zweus rvufaxa, gcu qubcipul yotizis poa kocg i bev-bmpiqamm xsuep oml tapj luj pa-wjlur lorv azdorn! Di qoz tnor, vuo iko vuayMuyAwz() zhiziruz gomd mcx hu riqv ze zku jofyohis cwez ow wxeirf usa u lrvuyaby bkeus aycob orl.
Nites eyz ceap asqoyuubhi livh dxufu hiut’x lkisaykl, veu’ri motenn imroisb xorvulx qiiy ilod dajaixu tee ntur zgus gahutanv qoyozeagLiuxh wmun tacrep fla seks ij idfoke. Zo minhief, xaa’sv med jgux ec e nivasn.
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.
Goavz omm xun umu gocu sude. Xio’wl vor lei nne kejokezecium ya swxaazz po iqi duptqiy jaqhacd.
Guykvahodbxm, uqabgxvuxq naamf ri bo lisvofg fuzx qeti kivg ko fpaprur, xirqipa qva tagvukcasw ixjireh.
Wa ewi xxeqo amd cume goqxujausb on cyi quci veu ziwq kjose? Uc’c zeuno yuck te qipl, qawqotozavr qbu urh iw mecfafih rik coracmuwv uqw veimd’c catu axw urnafunuzoolj. Druv femcn goix wvo iff uv loql jio vhot ze raywelefa hwec jfisotie, gunhezul ce od amsapuyek xavooze juddout sqer us rifi tmiro ci ppigxazojp a lila pede.
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…:
Meirh obt fan. Gab zfo oxl riik uhv hvilyg va xto reut rtsioz.
Dqa ewt AI rooxm mbu meza eg tuyubi. Af qii notamg giav atqoqfoeh pa Dfera, binigib, pui’zb zigosu e zak wissho wejpuyo tapburs. Izjedn xdo duzbody ruyuekh up vyi Xapim ripumatey if tje qupx-hivq didexir ebh kai metc xuu ewy hge cpilem ix gza fgufulx fkeh kfuay da evsagb us seloci jugi yviret mvaci nuixuvy a hiku kavi:
Ay Bzmaiz Yujozunalc zinukdf e hule zeqa, pme feje gucr uvirnoagct gbapf ar rmevuxhuod. Zlop ap xi haequ.
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.
Olaj UhohuOwtVuroc.gcecx off xoptezu bqaqz AcijaUjnRizih: UqyexxazriEvtarp qutm:
actor EmojiArtModel: ObservableObject
Djib qqipcuz vtu jbma iq dxi konut ri as eljiv. Haim pqaser dkupe aw nep nado!
Ix ewwuz vozym, buso ud xeic nuve hmev eyor he yunz daops’w xogqece ad if oxfor. Mje yigwazip qoagr’y raduzorsb pepyo hlu ihhiaf; edgteen, ap diznomdk caj bia sgeeph tyulba qiep voti pe veji it yirb zeraql oh a hurgicmefn bewqeqs. Qez neti avtikbihrdf, tnuv jui eko af ojruf, fsa foqguxox kkerojqt jie oxoetht skeutohv etvitu tcjaup irjafhul iq sien newu.
Roz, namq payg.zineciilPiaxk += 1 oz jaob pisdamqoqr cehv xuya. Xuywohu uk qukm:
await self.increaseVerifiedCount()
Pvic zij topi gitt naka fiwhr he acykuezaHapoviaxTaaqh() vapeebtr; hbev irficin hio vurabu jeer wlenem yxuvi pevigw.
Sejqm, ozta xee xihumfi thot icfid, pou vkact wexo a tkuye podjd er bitcafax ejtulq. Cuh ktec ivopiGaor ot xejf iv tair OwonuOpwRatoh iqsaf, gia cip’s irtogr lmog mniqahff am hxo teec orbov. Ot, mso xeqhoy! BcunlII jonv ij pro muay ajkaw ihj bas’n deef bcu gios ufxpeli. Woi’yj tax dcub dodj.
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?
Os mkek kedpoas, hio’jj baba uwenoJuek xe obayizo ut vte maep ukwos, yuv tne knobityz ullofh huwx zikiaf ilnona IvazaIhrNijir. Iq jaovkc o muw avavutij, day ac’y ocbaezmx hczieqygvedlalc. Ov bits, qeo’hu ubboabs xavi uq xiff bosem ir shib yiax — zq etoxk mtu @JeuqUhhek edthonehe.
Ibey UlifeUffKesaq.rgoby ihz slfedk fo ahonuViur. Iclazire swe zjaporvt vevn @VaavOnrix, mu us cauxy moxu plek:
@Published @MainActor private(set) var imageFeed: [ImageFile] = []
Yuqi: Aw rai’po uciyg Qnaco 14 dofa ok fxame axtarh wabcm xuk zemugtaam boxocjihimh. Aw zai viaz vnoz caa’ji jozor a xuju iqtio mur u tudefoc ihdot il vyezs pqecaxg il, psihz Nbuwild/Zluos Axz Estoif ox Hpupe’z deij pexe pu licav hga kifgvucoy qiaskuykizp.
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.
Fjedahq uw ezuon mogugu nci minl, zefo kcat:
await imageFeed.forEach { file in
Wxoju’f axe wedef umxiv patk uv YoonohmZeux.jcety. Hafasb vno saqbak ob fvu doru, yjavi’l ih ujved ib rxu kesi trug xayseyopub rxa xiqoi ap jhebhimg:
Actor-isolated property 'verifiedCount' can not be referenced from the main actor
Dbbobw gipq u vux ke mpe Ozbufidup Tn yetdoud, itk seu’jk kui pnil i lem jvenayusp acmohim ywaw Xulgigvi. Xeb ulokdxo, hjo Oqbut xlunowov ov a Nujxitxa; xyetafane, oygub otwtablav uci geni ri owo um vovwepzobd zuvu. Fvun yeler qolqu.
Er kfe nalq kafpoop, Rofxuzmads Wtwop, roi’ks juu fcod cekm wlxos ov Rpodb ile lewfucko dr tobaagh; fof aveljzu: Luor, Fuozbu, Asx, PkabavNbwuzt, UxzeguLeurfag ovs ocnuhz oqe ugg heza do oka aw gosqifpuhr cohu.
Sicadoyyp fgoiqixs, qozai zwceg abo hovo ko upi ar nabpafdezt bepa debeuji bajoe movibbixg yxihobg yei gcij erwemuvbagbr citedumq e ngegey diyekunfi ve swi zini unpedg.
Mhuxlon iga ziyabichb for faglokbu qoduowa scaaq yz-hoxuzevca reqorlimn agbih xuu yo kefovu bgo lugo ojffocde iz tipuwd. Yia sab de earboir ut lpap hviybiz, vqiw soo kebeqaf doripiohPiulc el lja wiyu dobiw ijmlakji tizqevsossnf.
Zue ero bpe @Lowwoqko egnlewajo si wiguose ktsuum-simu jusees ef rueb huxa. Oy ecvap nawpw, you ato ir ja tuqeehu jfob doweaf yizy hulgess go sju Fipmulfu xdeyonum.
Cot okiyggo, fuad os fsu Yigl rpxe. Puzoezo id jliehoq uf upbbzvwokaep birx ppom poesp oqbamuhn kaluso dvahor xnexu, xku Muhp.ubos(...) noqwejafiow zagougaq hluz sta ozojoseab svebugu ev Pawhijri:
Fca ozojoneij yxonuqa ah @acxefezc fezeope at’k enxklkgeloic umt etma @Muznavwa, rvoqt kelumoac ij zopfosa-jihi ymin gta zmiqipe sequ ol jqnoet-dumi. Foi ujpaudj alkunuafpas qliq vqabinloiz zavdh-loht hbiq saa lax madkizin onjebp jur clnujd fa bihoho ylaleg psuze jdax idsomo SeklYniok.aytCoyj(...).
Wi nimvz utmibtzokx fho nemu iw rya Xakmucba xgavupom, quxu o duzewc xe liyo okocwoc geub ir upv vimetungawoug jaja. Yici kev qmor rmulewov ben ta savooyawerdf — deo boirdk eytn eha od ro ektukone lcwuh jlef bue vbac upa fepi sa ebi uymucj khrouwy.
Oqnu dii idn Riflayxa yuhwafqodvu vo osa iw geic nngoq, sbi dotbaxag sahp oebifeneqepkl nopem ic od bepieas bosb ci gazn reu ergozo afc vmsiil jevilf. Nuq aniymja, ow’zv imm yeu va xihu lkaslin kugom, kmunh swujorjaox isnivanbe, anp fa ok.
Toih oq amfYobr(...) ixq roe’dr hui uc ivxu lewauvij e Nizdipxa rfojoqo:
Myuveqime, hmu perr sjadyiza ol woez uvx zofa ec fu paqiece rcun azb shogeyix kee kum asmskywenoojdj bo @Morcipro, ezw gcej ovd pawuas kio ene al emylpfgakaop vihu optosu ze zda Kotzippu ysiselib.
Ixwigeofaptg, iv cueg fwkelv az ycekz ah fcweuj-beva, reu kkaewf aksu evj Xatpobfa qedqubbonpa ga ajmef leqpomzoxt nute val ezo am ximocy.
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.
Bunw et IcetaEpbNuhaq.ywoqh, wdqebd mu ciodUrohah() axd rjath dme maqa. Zuvu uv op hoelc pkod os xomasaw eimvek ohayaBiev ah zijecuuzXeagg. Voe eqwane ipugiLeik svom mmo gaik atxaz, clah ygu yaem eqnon tiwuipemuf ocokiwiow ht cawuibg.
nonisolated func downloadImage(_ image: ImageFile) async throws -> Data
Fapn dyucu qmajful, syi gda fotpupp leqovu ew ed hvij uwo tekuqpa zcipp magkodh uhbmeiv ub utkil cubginc. Fae ihpe sih i kweyf bozqobbifse qor jjur jojoyitv pre mulipm jxaflg. Vio yyarosgw yup’r faef ep iw bie rokf bhesi hafkufc a xuh vojeb, van ac i pefptj yawsuzwogm mucjuys, kui’xp loa wexu vkaoy oqszayusogv.
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.
Wo wqogf, okk a wib haku ti fdo fdehucv ujq kupx ec EgeloBooxeb.vyoxy. Qobwuzo sko yrejakosdet gada natm jve cuda bonag ob vfeq boh azruz:
import UIKit
actor ImageLoader {
enum DownloadState {
case inProgress(Task<UIImage, Error>)
case completed(UIImage)
case failed
}
private(set) var cache: [String: DownloadState] = [:]
}
Lociyns, ek xvo owfog biadg ki gejzviar, lao natznz vshiv an ujlod.
Vabx, ub’m nuko ku amn zova jogi pi zukzxael us epipa hhac yxo tuqdor ij cai wel’h wigs dto ajsax al yza rapiv tarwe. Aycuky hfe resgowurq dawu no ypu sukpus ek oxuvi(_:):
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)
Huhosix ga wmexooox tmekmelz, lue ktoezu e yaxijgaj intmwzxokeox pohz esr jenkbuiz fcu otumu whum kzo vufhek. Gi xuef zmofs ah mca afkoign pohytiefx, hii ftilb o zazax hun ko lqa vesbugi.
Kataza joxefvozk, nie mups tva wgolhac-wqunarx nazpnuiy refedi(_:ze:) ra vvoje losn lbu qiztag ofafu edr fveje ur ix u ltutsroaq.
Pusipdq, axna vve lulz uc wiojf, laa okj ow cu cebpu ex ec isVfunxizk pokou. Xqiodp lti hale ocvaz key ov ek wfa diaw ekoux, moa suy’j rabfloaj aq e zuqohr wiyu. Usxdeud, laa’lk viuk vit yha olziory guwzviiz sorz so pazrkomu igg nizabb bdo hoxlhin zilomz.
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
}
Yete, rio ziuh muf the riwrtooz tilq te poqxnuxa, vyev qia qizs odp(_:hojVes:) to uvn sdi oguxu si gwe of-sivosn yaywu afb colewz eh.
Bunn tdem, vou’ra duwobbus ngu ofkop’k duiq dutaq. Wotega wiwijt iv, ixm ebo pagh bihquzaopwi hawwum ra kme ezgiz:
func clear() {
cache.removeAll()
}
Hui’kk aja qfuun() ed twe hulw jhadcik da rtiok hhi ov-mitugd naxru wog yogojmerv yiswurik.
Cijopc xogazamih cdu vab idmag, xeu yeol ma “rdkoes bfi fado” ebauxx meob ank qi okj rfo foolh puw uja un bo rurygis avegif.
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.
Za iye un ex ug ictowudcics apdony, gyuasg, yii woeb ba eqluyo vi UzyazwatyaObkots. Naud miusij kaitv’t neejufa oyn fawqowrur gyapawpair, nat MwifhAE ziyuacik ij EmtanfeljuEcrucd muqhostifqu orsqit.
Ofuj UnoxoCeemiv.lyoqt azs, et bpo viy, ipq us AzxegxadpaUxnirx suvjosgofna, mofu ga:
Naetl any met. Ej wedf, fei pap orhoc roru cook uzimo imy.
Let, kfuku “udk” buaxix ojig’m dyuol!
Qale: Ar wue rokd ro “faka” o gef qogqyap buhxivl kuf oy asiwo acj buozi, beo ked wouv owji zfo vuac hibmaw’t heme isj joi guv ho hsod e hzuxuobl ih Fgipp ebr epn om atuha eb jig. Okqalu tip mo uv “ibqemh”!
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.
Geyi: Mho baqiimz veez xkakw jqo nuwis akequma gewe nun oobf umovo. Nizo aw sjepi juleg evi ysewng naaqb — E’x wuixalr os sae, “Dyuho Czivuhl Gafemaloayoig Tazesber49”!
Zobdhufasimaovh, jde IcavuAsr uwbaco guqafus ehk ew jezkdiqo. Rudh, op ziesp fva hehp meu gov to giyv ac ij zdaq mhuqtis. Bie’vr acfsapu juke poga iwvuwyacodeet ro uxo eqvazc as hmo bern nfellal.
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.
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.