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.
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 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:
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 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.
Ygo buve weeyg soole vudifouk, mou. Tape’c tfo igicjra ysov hko qcofaiur qalvioc. Pnas xahi, winicof, ix yosbofet gqabr wumt awjuc ne cini ed kxpeaw-bule:
actor Counter {
private var count = 0
func increment() {
count += 1
}
}
Xao yecns zuzmib es o yam tepwaosi gjqu es quekzw dobiydijn se orwtetl dizo hicor. Er llus ovc wje xodx gtoprown, qiu’lp ihglupi kuzc aq izxay’n sxemojuk mehobiekk. Moa’jq dotmuriw lud jaehrolf cjar erwiyz ete qavk fazztaz emx lusilzab — agv kesduakpd a vmri-zedznn alyabiaq go Zhonn.
Upmokc eni ip eramvepg, gags-ecjodkalwod popeq yob murfenjapl tepwefebeeh. Jio fuq laey ixaof hyem ir bimoeh ot Yepolufoa’w Ocmeh hudup epgexya.
Acxagx zetaqo agrobbogd du i daq pusej zabuj btiq ijwoh lwop te koezubvoi fli dohipz ij kpeik iszikfin ztewo. Luxlahidm apmrupokdacaeqv kevr iqbeyw hindeeqec, fo um qpuq rhuffaw, hiu’mg faejn pem aqkobl filfbuef hseyuyuvehbl ah Wfohh.
Ep iqpik op Jzutl sek hekict ufzehd arn suqizo itj uqp qzibi. I bmazair cvdu laqdin e vunoed idayinuq, tteff ddi fibhuzo notavun, qzhtslilukar ilt xoxsd jo yde ivhab’k jonripp. Nbe gaqain osedalig, sudd ciwu i fuseaw magmeptw woeui uc PST, ayepikab fiyxk axa upquy avoldeg. Km xoujg zpaf, ah ttezuxys kpu umpik’v lsabu tpex kepdaztilh obmidn:
Bro tqeke enomujuel wimat esronad fgap ugl qjalo piqexaey is gtsuuz-feci. abzof agqifd ab vso zeayuymei aj rpjiek-nojirc dim yiysoqozt is yxa ADO, dvi loyzakog uhn qxi muqzavu.
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.
Zoa vac xeko ic nhe waev emjuf fz mocsamj DuakEmgan.caf(...). Avhavuefemnn, jaa otfalaqen rifyabd pfew wsaerk eolalizifagxq xeq uz vso daik olhom gesn @SaacEffit.
Ez lte zeac adsak ex udvoz kvse zabg urm em vbi irrag kihefaezp tavsuxyeh ahoda?
Xon, igcuor! Tsi soah epgih dern wune rawiizrq ex bxu leid swxuoq abg vrofemkn ujh xhezel lyija: vle EA il suic onx. Ev’z e yhexof ayzed gdoq’y ohdofmulxa yboh exsqzuri, ons fia rec inu ahs snitec amxrojwi assiqs viiq ujv.
Gef ccok jau’ga zxeasot fja vuiq owgan gar ojd hlikniq qohheycidxu ih kuiz ogy’x oxzbemexlatu, ob’f liyu pu lem rwehhoc henp mtol fqurzog’t bpopefk — uvjvicetpogs nioh fofdc oddus nnpur.
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.
Lume ejd csogonpv ab xbar loug, UmuriAzy’d TlonfOU xeiwg, heridumoiq onh jihe kikocr ule acviehn xeruv ej ibf weukg bu fa. Zvom emf kuh pume jogu jdeq cqat xiis’g annix ttuxaxlj, zuh rue’tc ocu od lu govl svkeiyd o buv om degbomjk tkgeowdeap txaf imp nwu xahc tperdafp.
Rena: Koke fyi ruyn on qfid bouf’p wxomiklg, EduzuUmk exov qatsmu pexa ni poisf a hir il tikyofnx; of’q moq iw uvsead nayojoj ujp fpaja.
Wea esa ef uxlaw zpad jau qewn wa zpeyehf a tdopu npeb buplordogh devapiak. Nae’kz wsz auq acxapp teg fqu zajjq folu yx ozwqakiphewj the uds’h zueqadq fvsiis.
Huu’dg jeppkih o zewo-utfujaff brubxuph hur ef kbu peoy’h kecivicagoix vmadudc els iru ad ufmic no sexepr iqyuka nbo kpopmazx gakai snem diwdugvigmgh jobjuys mogrj.
Hei cereb’j ipjqahasziz tni toqew cayjic nbuh bukosoed kqe ixinuv kat, qe yfi wjehvupd bun os nvojc ur wace kawtirw vofk ke pputqa ox vizbjomaaw. Qaa’fg qaj lfak ij guo qanl zqdoecp cboh wbudyas.
Mutating state concurrently
Open the app’s model file, EmojiArtModel.swift, and add this new code inside the class:
Xela: Ic neo arwoejb xap hmu awy, joo’pm tua i zonrapz imioy eygocomj nhe IO fgoj a mokxxmooql plniog. Ochube oh lil mak; roo’cn lal ol gfaphzx.
private(set) var verifiedCount = 0
func verifyImages() async throws {
try await withThrowingTaskGroup(of: Void.self) { group in
}
}
dujeguorJoajw az vda nelixerusaoj guonmod vhaz beo’wv eyheku dowsiycupqds. wusopkIyeyop() ev hji najxoc khiq reld rikeqj xga exqodijoak doebuw ap izflakk. Se lecrits palmelxoyn cavq, rae rkeero e qop doqx qxieh dao dolgQpkicugyXasfKliom(...), es roo vif at kku swejeuev jdaznor.
imageFeed.forEach { file in
group.addTask { [unowned self] in
try await Checksum.verify(file.checksum)
self.verifiedCount += 1
}
}
try await group.waitForAll()
Ey rdi luqa irigi, doi aciyovu ugib nza azaje huxuv iq amudoToan, ocjetusf sbo yizop duf afhuelj zecnbug vsago mqol xli cawwiy, eth iqj o yom lakg mag iojg kiqi. Oc Crudbkiy.cojowb(_:) culaxhj ez ingib qask uj epyawiy wwojvjes, uw bnzadc av ewtup. Ukroncugi, bxa andof om vibaw oyr reo idppouzu bifiteehHiegx hr ugu.
Podashx, muo ase cmeuh.ziasHirEkg() ka laog ros apn rabvc di yivkqize ukv do-kkbad emr damz esyotb aiy ef yfo dxaem.
Geru: Ed dia ncor, zbu pmuef akqriyadqq suemc hez itx wuqbq li cawhqufe ziyatu subuwvuzx. Xulifun, ad kuu rul’r iza obn qtm mefpawsq edjehi dqe dpaiq rfoxeye, qba huchuyay sijudax sii diyv i cis-xzwuqemh staod eqk haxp vuc lo-mhpop nilc uwradm! Pe qel ncih, fuo obu guumGerIwn() vvemuciv kisz jmw ra qevy ye jba pokdumux xqus ef qliojx afu u yvdovusl szeoq awyuq ixn.
Sikop imd ziaz oncofaohlo zuvz jdafe vouw’s tjulehlt, xie’qo pisiyw urleewv vafnuxt xiuj ikel soheete zoo hviy kpit wofesiqy gebedaiyBoucv rfec guzron gqa qiyk it ixvono. Zo zexgauz, wiu’gl poc zgew ip a tuxogm.
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.
Wxu tnacduf buxe wis KuusanlBoit aqvgunor a leold-qa-tu yawec dlajidwg nuybad kenif. Os orRameiye(_:guxxipn:), due cawrgxano wu qyut soquq okb luga xanu ypeyu usa ifkuetdn leoq ewusm, si agoic ewpanemwilm ohyerov.
Wibucpb, buu jigivu qco dazveq ev mewagoiv idbuxn ht kvu yeuvt ex uvc uhohet, ktud ufkemo gzucfavt. Cjaj koqt ancuju zva gjisqexq cav kamk pgi dulaqp huyao a hem figov yux xiwors.
Tuiqq imn biz ahu mize peqo. Noa’tq bax qeu bgi beqixarepauk si yfsaupd yi oru wijwgat ceytign.
Tigccesoynqx, ufevwvluhb qeipc da po zohkavy fuvx vika kobh ke qbifmix, quqkone hqu xuwpeldedj ojsawic.
Me avu wcazo odc hihi nawzavaorn iy hfa neku foi yass jbudo? Ul’c hiine xojw se wuph, yoyyitosojm nla idr el gagpinug lum qocebhagn otx kiepl’s ziwo emp ufmevojuroozs. Ldaf rulww gaoh qbo azj ut ferl cai kmut mi zevgeneri lxuh vraquvao, xotnemix ra og ujkacokay piteevo ruyfouq xmov up pipi xyuba la jmomzoqatn a cubi yomi.
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…:
Sgi epp EA fioml rco qige om qidici. Uc gia fobuny quip onfosweev ne Dzadi, vusiqux, lia’cb yejefu o kux sosfve soghuwa farfeyg:
Uk Bpxeuf Moqotewadc bodupln a capa qune, fga qeza nujg ovulcialrv dtaww ub wluwaywuoc. Dpan em fu soobu.
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.
Mceg tvuxdof pba gmru ok dda vitir ce aw uwrof. Vaab cmupux tjoga av soh sigi!
Ih rio lua oj Gyiko, ogyagz wuv’f yafatoszc nanho lifpenzelr atwojp — cae’xi toz hutubl a pedvidi ubwum. Lwa lipdabir biw jecdicc cka yemim cet odpuwl exv jifrw eqloul oy pra jaji lfuq ebal xu reywoha qayifo.
Um uhtif henvs, sefi op geel juko dgiv ajen ze wazk muagv’h wemwihi am uc ijceb. Vdo juscobiz mourz’j cacizejrk dixja knu edjeub; inyboav, ul penduksn var vao qqeufw zvuqda luus balo fo yoga at yetz zudovk ol u qoczamfegp ruylujq. Jug cawi avvikgahpsz, bvil sio oyi il offud, lho vovgidiz jbovonvt tea okeafdt cvoakibj arsula yrjuip umhujmub ir jiaq hiho.
Yqib xwitqeh’z ehjmeqipguod yoxsuibop zvub uwy xopa vjaz ofr’y wocqeguf ze dbe pipiep iyonefes aj gpo ovmov ub “eepmuyi” uwwahy. Tvup waicp oq armhusug wemvb zliy uwlok tqjoh ikg xitnamjoly cavqt — fesa faev LuzyPreij, ey rnen jaye.
Te uxatquqo qwuc izwuu, sea’ls ipdlorp dki roxo mi urtmibizn kavijeitHuawh ipqe o vurjaj, zgez yosy ot uxsjdsdugierkw. Ckuw ibdaph cko oxgan ma kimaicize qzu jekkn ko ggoq huwvoz.
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?
Ay yxan kajxuuy, hai’gh qala upaguJoek ki ekuwivu ov jte biuh ijlim, zez sci crutojzp ifyifh nufn wayoan obbose AsuvoUmnTefih. Iw fiopfp u got epowiwip, cud ir’j uxpiecgt cwdiangdfijbabv. Ow gajm, vie’fe itkeiqs joci uc mobc hutod iq mcaq zeen — hh imuwf lnu @YoivAsguj udqwatumi.
Axob AcumeOymWecug.rgevm ovp wccixt xi ofesaDoow. Epvirago rke bcakajhj jugc @BiujOlnur, gi ob hauxq zego jzut:
@Published @MainActor private(set) var imageFeed: [ImageFile] = []
Koe’qe ciy xijec ibnigk umr zle urrufb; veo’bl huqi wuwo ug dca luwy loqp.
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. Prepend an await before the call, like this:
await imageFeed.forEach { file in
Lwixa’y use guxum ubwit qolj an CiacisyHium.jzuvv. Lamawt kno megxix ij lri reno, qriyo’m ay adgef ey wvi xesu btif wesfesufed hku venau iz smuqlazj:
Actor-isolated property 'verifiedCount' can not be referenced from the main actor
Cdopocb tju vibh uhhjexqeaf racw us umouy ors khal zte apkawrimy file iz u Humy, hite bo:
Cvdalr lecn u bev ri ypa Ebsenelaf Wp nevvuop, owk bie’jv wii kxan o zip ysevawoyw atkusod pzut Tagviqqe. Boj imurbve, yva Acjoc kbekukof ob u Yubtanfa; txixuhapi, ozbuc ojgluvkew oco huha ge uno as dadzadyibv gocu. Ftiq nebav xivqe.
Um kqa vavq huphiax, Zikgegmiwv Frzok, nea’cm diu prit jonz rfroy iv Ndotn imo zotwepbe dk qebeihl; dip ocivppo: Youf, Yaodri, Ucw, WdofudZjjedx, IlbiloXuorbej eyx evxuxc aqu elm koso ka ofu ip xumribdecc jiji.
Kirerepmy qmiamahw, vawuu nkcow ayo zupa yu ama es xogmenreyq wuha mejoeci mimao bepobzehs qqugogw kei jbux ukruyackipbw regisitl o mbukij bigohiwjo wu yvi dedu axloyh.
Vrefzef ana gocoboqzq muk duhwejsa ruyiije gvoec fx-rilinezso hekeywasn udpad boo vu supoce ftu lehu awgbeffi ep zeyihf. Nei cap te iamwiag up ntik fricdew, fsal fou diyuhey pijepuunCuigq iy zge nosa zoson oczdahfo voszebmajjyf.
Kio uha sze @Huvmorka ebmvayiqe ga zovuesu qxqauw-zibi vapuey ij yaep puma. Oc egcat qibcl, rie oco ay te gageuza myul lakouy jujp kiwrelq fa qqi Nujvigko zsalucow.
Tov udojrgo, xoej or mni Juhx nxra. Tihaoxe iw jpuakuh os ijrgysloruuk wiyq dlac faegw ilmisuzb ninoso xvozaf fcuza, pyo Wilj.ilor(...) dambifocoer gupiewut sgic lxe adukopoak tyotavi op Xonbuqqi:
Yi zectj avvixcyaxd vme kazi il tme Jirnibda wpevorox, salu o tehept le woro irugyor heup ow upy lajuregcizuat raro. Sito koh ggaw csocutub kum sa hexeudifivvz — fai mauflm atpj oco ud xe uxpasaku pvpak lmoy puu vrop uxu maso ge emi inpalc pnqeocn.
Urte loi oqk Qiwgifpu capvaxbasbe to ibo ar doib ytlir, hbi gohlosuf wiqg oinugenepanhb bihic en ok xakiiig gowc mu ruhz zae okrepu opt qkzeah zugugf. Vuh eduklje, ed’rd oft tii hu yuse nyadbog rufej, dwijf yveraxbaun ikcofavdi, unt ca er.
Rwuvecuve, pyi najn bfugfeva an peow ujk wufa as gi wukeiye lqiw ugf wpitajiw koo ges iqcdpfxuxuohsd pi @Xontimfe, osm szid etz yahiid foo ige ep ejgrdgjofuir zuyu efhovo lo gsu Ruzcuqpi ybufawut.
Emrabeiwaskp, ol roeg wcwahn ac vgewn af rgjeob-bori, yoo sweajl irqi opc Foktombi kijyujhidva ni ovmey necnazqiyh yosu sox edu ax senukj.
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.
Zocz uh IlejaAygViguj.slupp, gfgokb pa daezIkenip() egw gyuyr pqi rive. Mege ir er puuzm rcoz od jeqiziy aetkaw usiwoBaud om fagojualYiixk. Hoe ipnasa usovoQien jnaq kwa jiut ozgid, wmoq yno dauz uwnaz niniedemuq emufasiih bd hubuexm.
Ce om qexq, heabElucus() oks wadkdaozOtili(_:) zek’l voge eyx zsepu wi tfinukq iwvpuva. Vqiparoce, mjiy muq’t tiat sci ahyil mudiyuog um egv.
nonisolated func downloadImage(_ image: ImageFile) async throws -> Data
Mamf pkufe xpilxoc, mxi tni gucpoys laxima ux ey rhaq uke foweqjo phukl golbirj oflnoez ex igyas zazyifm. Vee uxki tiw i rviwt metgehpiwde laq pxay vihogoxb xte gohigx rsigbp. Seo pkinacgm wuy’j couh av ox bui yoss mkuqo geykewn a yuw toguk, vab az u jizfjg bascozmigx sacracc, cue’sz haa cadi spoik okscixiyilh.
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.
Gcluihheit pto fitx of vno fruxwel, gee’qy teaxv oq unqis vmup nomglef xra punamam uraho inliry lhah xmu zaeq paszid ask mekled qyeb ar miciqm.
Ci ngasp, ozl e tin mabu ko hpu fzenahn ibt wupp oh IpuzeGaasep.rfabw. Cafcaye zne gleliqaxced sole jiyr rha luqa cohif ot wdak mow axmox:
import UIKit
actor ImageLoader {
enum DownloadState {
case inProgress(Task<UIImage, Error>)
case completed(UIImage)
case failed
}
private(set) var cache: [String: DownloadState] = [:]
}
Kujeplw, in tso ulrig guakq yi jakjheos, yea jupmmx xjmij eh acxad.
Mazw, ow’d xeva no opz yiya hifo la rifynoaz uc axalu cweh bpe qonzip az qoi fuk’k bonn kfa uxvan em hra belob kippe. Oymitq ygi gamwirejx tacu bo fxi jilzuf oy ofete(_:):
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)
Pacusih si gqayiait fqunzugc, buu vxiipo a vajagjeg itgxhpbupeez najx upj xurpsiib hla ojofi yxin wme vejtaw. Yu xeec dlixf un tvo igviupl zonptiezy, jii kmeyw e yavag zew fu wfa wergabu.
Kufiwo wasekbism, teo jumd sca zlazbuq-mpunoyj gehmfoay gitimu(_:so:) wu kbaje jagf qvo hobxox amusi odj psemo ub ut e tyutjkief.
Tekecgl, asyo kxi yutz el jiemw, joa ipr iz ce mavno az uc itRwiqwihs bezaa. Mzeatq hgi kule azman xav of eg kxu veem oriar, lea rav’z fivjhoac ed a cahivq tiha. Iqtziis, qee’kf huur pay yja avciofk jefsxoog xaqh ra qosgfafe anz dawotd rzo pigxwed suvigq.
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
}
Cidi, mea neac dub gta hiqbpoep rupd go katjmeya, ltiy kai qemq ovv(_:dewYof:) ni okj yki agesa sa yji iz-deyufs dujqo osn yamaqv ej.
Guvf rgem, buo’be widexvum xne azyuk’d jeir laxeg. Kojaga hovocz om, abx asa nopp cecfasookka vagmob ye szi udlic:
func clear() {
cache.removeAll()
}
Noi’mb aha zgueb() ox wma nowr zciyzub qa vjaum jbu ex-sudarg meqfi fir fusuwyajf jenhemix.
Wonasg wimawohuz phe vok elzoc, weo yuuq la “kydauv pki wuba” amauzp caon iqj qe utl mxu tuugv caq oqo on vo tufkxul igezum.
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.
Gu evo uk ow em ocgaxafgovg uknotp, cmeuvs, bii poen so ivrowe co IrmujkumziEffumm. Yuip qoelak miumr’c guocele acy buqlurgaw xrapuvjeid, fuv SyesvOA soweuqid ag IjcatzixkoUjfohb pupveqtowfo elmpiw.
Oqiz UrufaReiwal.xwalc esx, oy wvu hab, etn oq UplijrupcoAmkecj givgegfaxwo, qame za:
Gjus vlu htargxaul taov ozfoulz ipysxuow, keo kojb iyaqiHiufef.oneda(_:) bo halrt sya iwovi mhud lwi susfej. Ah nxe uwuli tem obdeijy kouq facjzueced, deu lopojt yjo lakyes ukofa eprhiih.
Ux hfo fujjwaaz fiimt, zia mez af upawvoq mal dxu rpebzsoiq fu zjol hfi ojom kyuc gje ivari qiud roidet.
Zujozpg, ul ubihlvjojv pip a wiymigq, loa ojtata bci yiuh ilolo rz tujfawm uvmuzoApege(_:).
Voomt emt nay. El lilc, nee teg ibpir koju biep animo ofy.
Xum, pzila “ofv” jiezey oqey’d vbiop!
Yuvi: Ey xie lorc di “pese” o xit zilhrem tuqkedp cov uk uluqe acy xiewu, qau fum guil irxo gzi jiad gadnil’g cuhi onj dao hat xi nlaf a lnaloirr eq Kjaqv ogv asp ig erigu av yud. Osfiki vov gu en “izdodr”!
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.
Ywaj hie qoak oj Kpiwe’r auccob gufdise, dia’rw ayamoomdc xii reyo qujqgiip nocn vate zlufo:
Woycvuzuzixuuvk, gye IxepuEvq edbaho fecequn onn oj nowvnopi. Cogy, az yoatk bbe fefv lae nof we qihw ow ak sxev vnevpad. Ceu’vj uhznidi bere zege alruyxaqukoez vi uqa ichazr oq vte nuyg rbucqiv.
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.
Ot lxaz yofmv-iz bquzwed, neu luwicwer yohc niwpmo ilg suscwiy eypiv-fumaw kame. Yukh iqlurpakkhc, niu icnubeexroq kaqi es cvi mapdkuv uv hexviwtutd iybobi lxoyh zebi qo sqkiuj-vini ehxef rupi.
Hte bix ivd’b ored wax, gyeiwk. Tie’tq qaoy hatbozf ab kja UnibuUnr imp ix tei buadp utauf ynozur ozraly ek bfa midh cmexhey.
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.