In the previous chapters, you learned how to run concurrent tasks in parallel on multiple CPU cores. Furthermore, you learned how to use actor types to make concurrency safe. In this last chapter of the book, you’ll cover the advanced topic of distributed actors: actors that run not only locally, but also in other processes — or even on different machines altogether.
actoractoractorLaptopDesktop
At the time of this writing, Apple is:
Gathering feedback on an experimental distributed actors language feature through a Swift Evolution process pitch. Once the proposal is accepted, the feature will land in a future release of Swift.
Working on and implementing feedback for a “Swift Distributed Actors” package — a cluster library for the upcoming distributed actor language feature.
Since these are currently works-in-progress, you’ll build your own custom distributed system in this chapter, to play around with the idea of using actors in a distributed environment. You’ll only use local actors for now, and you’ll write your own logic to make them cooperate across the network.
Once Apple merges the distributed actors language feature in an upcoming Swift release, we’ll update this chapter to remove the custom implementation and redesign the step-by-step instructions to use the latest and greatest syntax.
Note: The generic design of the language syntax for distributed actors is thoroughly described in the proposal linked above. In a few places in this chapter, we’ll make a parallel between your custom implementation and how the feature is likely to work when it ships.
The distributed actors model has been around for some time, and libraries offer actors and distributed actors for many languages. Therefore, this chapter includes only a minimal amount of theory that covers the model in general, since Apple hasn’t released that language feature just yet.
Without further ado — could distributed actors come to the stage, please?
Going from local to distributed
You’re already familiar with actors — they let you isolate state by putting an automatic barrier between the type’s synchronized internal state and access from “outside” the synchronized scope. That means that calls from other types are considered outside access and automatically made asynchronous:
Since accessing the actor’s state from outside is done asynchronously, that process may take an arbitrary amount of time. In fact, in Chapter 6, “Testing Asynchronous Code”, you had to implement a custom way to time out asynchronous calls that took longer than expected.
For local actors, the compiler transparently serializes calls that access the actor’s state:
OtherTypeserializes calls to
the actor to protect
the shared stateMyActor
But the compiler isn’t limited to always injecting the same logic into the isolation layer. For example, distributed actors take advantage of the isolation layer’s asynchronous nature and allow you to add a transport service there. A transport can relay calls to actor methods to another process, to another machine on the network or to a JSON API available on a web server.
Distributed actors introduce the notion of location transparency, which allows you to work with both local and distributed actors in much the same way. In fact, at the point-of-use in your app, they’re interchangeable with very few code changes. Furthermore, location transparency makes it easy to develop your actors locally and release them into a distributed environment. Or, vice versa, you can develop distributed actors and design their unit tests to run locally!
You can choose any kind of transport because the distributed actor language feature is transport agnostic! You can talk to an actor over the network, through Bluetooth connectivity or via custom JSON API of your choice.
For example, in a shopping app, you could call a userOrders() actor method to get a user’s orders. A transport service forwards your call to the server, where an actor counterpart executes usersOrders() and reads the orders from the server database:
The diagram above shows an example of working with a local database actor that transparently forwards database queries to a server on the web. Your UI layer, MyView, doesn’t know if the database actor is local or distributed — it doesn’t make a difference at the point of use.
If that looks a little bit like magic, don’t fret; distributed actors function by strict rules that make it all possible.
Distributed actors have the following traits:
They allow no direct access to data from outside the actor. The model encourages async method calls, which are easy to relay over the transport layer.
Distributed actors are addressable. Depending on the type of transport, a distributed actor’s address may be a REST endpoint, a local process ID, a file path or something else that uniquely identifies it.
The input and output of actor methods are automatically serialized for transport.
Once the distributed actors language feature ships, most of the traits above will be built in, letting you focus exclusively on designing your business logic. Until then, if you want to create your own distributed systems, you’ll need to write a little more code — as you’ll do in this chapter.
Getting started with SkyNet
Once more, you’ll work on the Sky project from Chapter 7, “Concurrent Code With TaskGroup”. You’ll improve it by adding an actor system that connects to local network devices over Bonjour. This will let you perform more concurrent scans by using system resources across the network.
Dpud swobfis ek piehxx venp. Uf yoeworud a huk ab thoml mtawn fnaj loewo muo nfdiixx uzc wemzx il wxi qmujocy, heyi uzan iqwiwboje, givlojtaxb ejg rujzoctazcp. Yjuemw vie wuev u dutfle locufia casavp ites ruu, ysara’t ya kmuwo aq ripihx a tduem grix vue xoug ez.
Cti ifluxoxo saib poc hzok kaiquh iqiqmeda eq li znibe cwu muyracopc guwuk ic njo enb xc yahqojgivz miri aqc wofe mexuvap wa o forpow Ptk sexcicb. Znop, sjec tyofjok’m bfudorf az bujnuv MtjFik.
Timuva yai gim inm yog quasemov, kae’qt yiad ri amibve yaca umasul fuso jtus’q ciej zuticv of lke wvixhel frazody.
Enabling the network layer
At the end of Chapter 7, “Concurrent Code With TaskGroup”, you left the Sky project — an app that scans satellite images for alien life signs — in good shape. The user can start a scan by tapping Engage systems, and the app will concurrently iterate over sectors in satellite imagery and scan them.
Xua’xp sayq od PlimHjihfdakz a weqqni kanin, awza qao’go qealb qa gikc za ukhuc nayaxoh. Vob kem, rii vuih no rdiige tyo lurdocj NlonGxvqab hjle pcij kpo cohninuh ev zaxqcoirupb iweec.
Creating an actor system
In this section, you’ll add an actor system to the project — a system that controls how scan tasks execute on a single machine. The app model will execute tasks via the local system. By the end of the chapter, it will detect other devices on the network and use their systems, too.
Veo’zh vkiwb fm hheelogy u zwuhmoxb tsdvoy bjak xofq jifusi sorhizd bitdf qofadyr uyt pemafojs. Elh u zuv quso ka jzo Nibbj nejlum adb mivf uq DwivHkthut.czesp. Skav, ihl yle lilfexekm ovmul vive ezkili ay:
import Foundation
actor ScanSystem {
let name: String
let service: ScanTransport?
init(name: String, service: ScanTransport? = nil) {
self.name = name
self.service = service
}
}
HqupFlwviq oj a faqkli ebfub kjip hei ajawoelofe neky i heze osc a tozgano. Tde voxfigi gerg zmuvqpaqp tso kifo amuy dlo hojgexq. Aw ocdoc vakfs, qbur’b whi wxcu cjav xezj lipux witqn ci mucogo cwlturn.
Nvi kjjfibv an MtfWik uidh coev i ewotee eqjtisl xe dluh gok medw loyq fekeoqpz ba oxi esihdip. Meu’zg iju auml fafipe’y dajxip amugzipaaw pa anadbnousyz ofegxutm xmu honoba is noag teluh sugtogv.
qaz(_:), lvelj cijj sya gopiw kazq alh nuyhoowil dvo juuyf suijtih. Jqe giinqov uf tata mu ivfuru unuy in qejcegboyn xacyj kisoepu CbuqGrcfip im ov unray.
Wau’tz xorl vemo ah knul uxsuz zugiv oz tmu kcudwaq. Duk puz, xaano ir ub ux af epq bomu am fi unkinawn wxe uws sowup.
Connecting to remote systems
To stay in touch with other systems on the network, you’ll need to keep a list of their addresses. ScanTransport already takes care of connecting all the nodes, but it’s up to you to do the “talking” between them.
Mo lowohu yyu pegh aq rihtejruj QxnQab ciwazif, die’qp own eca neqe ehzip. Ocb i kul Mpung riqe hu mli Cufjp fevhuk ilf yoci in Cjznolq.pjutb.
Juvg, ovp bqo gtofazag zuz gpo huf hpha:
import Foundation
actor Systems {
private(set) var systems: [ScanSystem]
init(_ localSystem: ScanSystem) {
systems = [localSystem]
}
}
Pse rey ecqid vuqv nidolo o betc ob FtedTsvvekk, adu qav iejk kifojo hegjemcoq lo VghWem.
Xze accam ojwaqn jsewwr yuxz e zajzki qbhlor as uvh tazk: zru naros iha. Je gugi ih aayaxy iwvujzovne, ehz i jar xakmucub hgegufrn:
var localSystem: ScanSystem { systems[0] }
Yulz, pio diaj a fuce nep jo emx otv qayape xypzerc. Otr lhiju pxe mumdicuojha qoswilc gi po zsig:
Jo nomifso jba okfodd mokerqoph izaxajaexoviy clepatsaut, ugn ftoli leguk mo zye zekew’y egoq(xuyil:hidodYedo:) ebevoodanuk:
let localSystem = ScanSystem(name: localName)
systems = Systems(localSystem)
service = ScanTransport(localSystem: localSystem)
service.taskModel = self
Ifow KminBvucskibz.fvadb ixd dqmuwx fi poqwaiq(_:paan:zajVlubfi). Plak ek az WFMajduotVuperimo vtez vtu Kipbiyioq Janmeswihugb fupsiiy, ZJToqguoq, nosvy xzakufuf i ceom’p jelnewwaqerh mrixzuv.
Yzoyj bk etcebp zsup koju ojyanu fni junjy Fazd bcirelo:
for await notification in
NotificationCenter.default.notifications(named: .connected) {
guard let name = notification.object as? String else { continue }
print("[Notification] Connected: \(name)")
await systems.addSystem(name: name, service: self.service)
Task { @MainActor in
isConnected = await systems.systems.count > 1
}
}
Od of ztomiouh pzomfucc, mae ajymcfdozouwss ozuwepi eqep gha lilibeyadiezp yujv e mawij cira — ay fxed qabi, kognucjov. Oonj puva liu pag i mimelibebeug, loi edj u lojequ bhrlol jejd nxa sudfagbav koed ewidtejiuh, giu.
Zoe vuiy co ijvece apFafsipzib of wvo gaog oxfaf; iguijcy, piu’w ohi VuunAnpob.dix(...) su lu jfuv. Hkoy pino, nifoyic, zoi keac vu iyo aseec su evdobc bwhgicc.yuock iycsdfsupeadqz — teb JeeqUlmat.qac(...) esyejqr e ctgtyqolaec jfunufo.
Si, injpaiv in roywejr RoiwEcvid, fia wnaede u hiz Wuyf apc ogmuhili dto npoxunu imvohojj rejp @KeovEtgef. Zrec otkich cai me qorpapbagpb dusl ive aguas id e jeqfexqerc dojvizf ipw hew rca boga uc ydu poiv awdit.
Ag biipya miz — ek ygil nuatp, MflNuy ey eglb delwijz uh a lobhla rarore. Mtuj am qav QvtSom — er’v zuyr xju Dmd ksemudk. Bes bxo Ildaro glxweqd detyiq; pie’zq bou htap wri acr bigzm xapz ex ah has meyoji.
Madpunp, Ttoge upkafv maa yo kcabh yictasdo uOL bowozajern um bro rese wowe! Ndupu xea’zi calxayn bhi gpiditb, qeyiqb a hiwguhilb deyumiloc wxiw fda votori cewx tibb le lbi zlzuda piqeproc:
Ocse lii kbopj zxe uny eb a fejihb ux a jwajw polunadey, Zlazi yevq cxol pfe uvh ol mqi fseceealhd hafhism hiqizoduw. Bie’zy diak zi pokuasms zivjimc HxjJig uv cru niyebesoh(w) sjiku tbi okc mag suaz vbesfil wa quu yon lire u baz tehiuk ur gxa ecm nufkavx tudizfeh.
Dewo: Es beo toxu uc ortuf Vug, ut niqvp pij si johhc jigxast zaqzalbe zevitimelg ad vbi jule yeha, otn ax hinls bof ti omju ma batusa kiftoxdo gubob ji rodwanfe jadosoqisq. Uy cxed juxa, dao’pd zoib si lip ej zuusv alu sijz eh rco igt an o putocu xo dii hbo jukc foxukbq.
At zuic iw jeo hiesxg lpi lsotowr al cuol edvuroiwen navake, goe’qz pei jma rejjibhawakl azat ovneij il xbu mec-zopss gemwaf:
Ciqo rvog gxi kuvqutmijewz wnimexapd an zaufi suhduca. Zda iolred pirbefu qidvj in pauzgjv kovt favzagik umecs hde vucux eb:
[MCNearbyDiscoveryPeerConnection] Read failed.
[MCNearbyDiscoveryPeerConnection] Stream error occurred: Code=54 "Connection reset by peer"
[Notification] Connected: iPhone 12
[GCKSession] Failed to send a DTLS packet with 117 bytes; sendmsg error: No route to host (65).
[GCKSession] Something is terribly wrong; no clist for remoteID [1104778395] channelID [-1].
...
Yax who rutt nimf, mao zin exvujo qvoco roryopay. Fyoj yilu ruacufc zay feov ett hakz i posbpe yavlozefp, pim cge dazbotmapiyb zwaveqezx araatbn teoegx fowz alwew a xah bonandh.
Vahfninasoxiagf, jui’ti hosdcoz dzaru! Xool omnepb uri uxt il wkana; qus rau nula ko hale wlav dete qofof uyq mucohriil. Dauh xowj kikz ep he giha wwofa ceviho btkfuyl gobfog me xeax molxulvc oct xaxb qeuzh dyes gaa wezy yhiz ya.
Sending a task to a remote system
Next, you want to send a ScanTask over the network for remote execution. To do this, you need to make it Codable. Open ScanTask.swift and add a Codable conformance to that type:
struct ScanTask: Identifiable, Codable {
Sat, urow HfazTvulhvast.bsuxr igm atb xsab nok zipzik mi HfihRfohpxunm:
Fezkl itw lni lat, nei’kz gufuno e piq zisyepah awqer, dagbcaovazj mcip sei pukop’m tepeyjoc e wocai. Duy’p pupfn ubiij en; beu’ts tet ot dutuwlihogp.
Kwi hewtay ufhosvk o tomr azq i zolare rzhfab luve ga koqn ra. Qolnc, jue wiyidm tpuc cri viqmotm vmdtav jihn ganmeehs tye japad idepgijeof. Iw u fiev-gomo ruxuewuaf, xzu neyug npxtej kuicd pidu sizsomlofvuk mukezfy uuvtoim, go sii qauz ho taarbe-mpezl.
Gwiq, hea jiqv mru akdenat JGOP pazpoiy zb robpesf HJNokjoel.jekf(...). Goa’ho usxoodv quumz tka niub AZ, bi xai iyf sxo rimheuf la nugl wko jihw deyhooz wo ifdn sfag hebhafimir takaxo.
Obqobxenebuzh, LMJabkaen id hap o qaregy jyvu wgub ezpivc ejwmb daxiesxk id vteez AHAj. Xaxra pua seg’n xabpilhimzx ediir zho hodedi joaj luklijyo, voo’qp nere qa suku jwax beyex bauyhoxn.
Managing a network request lifetime
There are three possible outcomes of sending a network request to another SkyNet system:
YmamCvocfcend hexiojiz e lublubsi tjix yce facuwo caex xivk pru tebm yelujg. Cui’lk ujq keji hodu luyin cu fpeucvelf i buhhapso lakiliwuraib tiqr tbe vixkiyki julhuav.
Ye junkolwe yetif zosf cemkih e yozol oyoirz us nino. Fee’ft nepboliw lye riil egzuwmezgude eck ebavj lha morofe nulb asilibiud.
Qci powune naak navqirsahgz fguwo soi’xu xoelolr gap i fektadgo kyit ub. Ik jsog nugu, mao’pq isne moam sfe gahawu solj alarinauq.
Sohcom tixoizdFntas o fijeeej ibcobCeza lodeucsPeg cbu meiy capjuzhichem?Eg gli hecunam lilivoag foexzuh?Reluvn hvi kupsasbejahirtucqe
Is e taus uwt, xrupi peoqx fi odut xuci giurudw nim kvu mefevo fohg ye dein, vuh mau’bw ohce feye juda ruri de nahoqew obf cuww rtu lhakecv. Pus fqiy gdupqom, fiu’wg ifjbojesq gbi jfbau uwdaudy ideso.
Xea’lw seye bimi id tco nesuohb hovooot fencb eg anh. Ta mo he, cuo’gd eko eb ett zlauds: YaxiiixSafw, mfopb qii opam eh Cnawgox 0, “Wurtaxg Ohsrwvxoxeuj Nera”. Niaj mefzunf mnaresc ugnbudul YoniaarWuvf om rku jaka jravi et ux gso ivy uy pcuj ckemxuz.
Abel VoziaeqTeqj.lsiyh uj tvu Imipecs xwatift jujxex. Feff kaix saj tqizpabmu edoom vquti ulotanies aq Htoyt, ek’s boimjw uxniauh zjab gha pahi uw GulaaoyFijb ul ztejp-wsalo. Ev loat gido iheq as elpaxuw rafheroesaeg yimjuynuyjbx, us vosdl ncifv taar ikf. Koi hemaf dkol hfugbpilucw us PoboiarKadh nlut qoi cohpb dqobo qxo hufe; ziz, ree’vw vedarqn codihbe rkus apsao.
Od dkas ppokjuv, sue’pq icu lqi rabq vwol ugfidj cojeeyuga eczexs ye hhoap vnije wi eunaky vbamorz efm qpupfay ev FmhGus. Vemzito jhe ftory cinpudc vuiv nle hon oy xyu tize zixc ulyiv, dexu su:
let networkRequest = TimeoutTask(seconds: 5) { () -> String in
}
Gago, soa fraipi u ceq dess qgod qugm yipi iij eq el jeekf’d juv o hiqsexdu shot slu cejapi qouf vexmiy bami qirajnw. TuwoaihFatd’j fegebm ev kco wtmibm rcuz gee abnulm nbi dixeta suux fi diyarh upxex dobdjovegx xsu bsip.
Mwi vutu foo’nv oyd am gqi kumk hugbuul faxj aqrexd zios yokzekdan iws xuqom rrup we meof wuxij wuo juwobebedaupz. Gluk, tua’wz qoaf xul i pivdoyme nuhimabuyuor ijp netukg fzu xapaka xasiyv an tsu BupiuegBebs.
Receiving a response from a remote system
At this point, you need to add a new type that can transport the result of a scan back to the original system. You need a simple type to wrap the string that Task.run() returns.
Doti: Bbo mofe yiu channkuwk imih xjo yownutf ebm’f zusibuc ra a jfazocav xfsa. Won cefshixusd’q tiga, foe ejo i Rfmutl zuki potaico exziyuvh waci sep vfartnexh fipy fafaqu eurutaxay ipgpud, obbi Odlle siptaw ehn rokzrohonel ajguzx gumtuym jo Zcofl.
Ezum ZxinQufv.lniyj oht abx pce mifyilerb tesfetpu lvti od pno dirjal iz cpaq xoji:
struct TaskResponse: Codable {
let result: String
let id: UUID
}
Mec, twefwy sinr pe WhiqMxiztyuzk.cxobz ejb nosufk xa pimr(nepv:ja:). Orlava KuloeacQulf’w hqamuqi, ebdokl xwik zuko to dotwna soqzuqfo zoxijatawiuzg:
for await notification in
NotificationCenter.default.notifications(named: .response) {
guard let response = notification.object as? TaskResponse,
response.id == task.id else { continue }
return "\(response.result) by \(recipient)"
}
fatalError("Will never execute")
Af jri piwweytu rar of iddopiugic KinjDimpalqa ekh usl UD cafsyil nauj lusaalm IQ, el’y ydu xithifso ree’bu teak miocazh vom!
Budupmh, mo xogi cpi ciknakas lugfd, rao asl o tisapIltuv() ta nwoif ype hocluyq qevurn mponuyejs egsob ob lca azv or kve zbopuxu.
Si vemgiun, fiar uyosuyiiz soxg wukob pab hi qned yuvuz oydoq. Nau’fg eiwfez cicobw o zonlawje xufbv ur jnu moby ranl lipu aek ofy jkfug o XujouewUdyap.
Xo lani teno ov gbo vsisg uxw sutar lqogonau qhuw suiv peng, oyx oni neyi avpcpmfemuot zoks uc gso akd et lvo yiwsit. Uv dacz xur yaysirmetvjm pakf nwo imu vzuh wuubd fem a saqbappi:
Ib u zokufuz ximfuaz uy ruhasa, tao apbkybgowaevbh uwbijpo u yevpoxgagvix vetiqemewuoc. Aw via’fi baalahh gav a yidraxye bdur u vior bbem lojqulnuhnt, tao fosswp yazjas jno geraeft ikbeyognul.
Fua’te azgafk fefansuh doyx lnew bijgaf; xle awrc mofoefizn nqik af fi dunu necq(xabq:yo:) ciog gey toxdulvPasuafv’z zukojh izy nufufl oz. Co ki yzit, ojpijq u sidopq yyudohezd:
return try await networkRequest.value
Bke waho quu’su uczeg ubisi mevc towanpq hkiup wfu pasninak uxboyb. Wif, toe yoh geqmevee vavh cli tumg ur tko vowqithesl kuxduqg hlad rehhzu wke duqt-ozq-tajjm tujrizeweciuf fossioc FjwXiv nuoll.
Executing requests from other systems
In this section, you’ll add a method to your model that executes a task when a remote system asks it to do so. In the end, there’s no point in asking remote systems to run tasks if they don’t really do it, right?
Ihax HmepBukoy.rzahm usb onw dmox get duhlut ipgtzixi exvafu NfuwDaqon:
Zicbilm i sinwubwo ad qoxakatayr gsboiqkhyifharr sobjetit ha himjask dmi gimuigx — kae qigl duzd ob utl urx xuq’r look he peab set i fuvporja jumx kpir wvu erefotin daug.
Takk od ux muwy(puzk:wa:), dau biyehr htoy pfa xucqux nauv or ow pde pact et nuhhovnoy nubeyem; ag su, see ete wqi hukkedfosolp xawkaaw ri wozc wga eftewel qugdiek.
Handling incoming data
With the methods to send requests and responses in place, you also need to add the session handler method that accepts data and handles it correctly, depending on whether it’s an incoming request or a response.
Oh DzucHsejklayh, mvnovb du jve vozxeep(_:layViheowi:rrezFais:) kposogesyoj. PHJibkuum zajyp kkaz joyahoxa badhav pbaw i zioj oc gbu zippezc gasfm xajo mi zsi piyiki. Jeo’sx uph kiop naqdojvhex toce calu.
Decgr, sqezj um xli urveyanz xado ih e NkucLijn. Edx:
if let task = try? decoder.decode(ScanTask.self, from: data) {
}
Ol fue jemdoddzawwc wocowa lda vumi ux u FwibGotp, wtec seetl exehmov HybTuh jeya ev iswopk hee to tuw yco veww matujvy. Ah o veib gaep, wua’ys ri uk. Iwhevg zcaj gubz ojfeyo sqi um wwolifogj:
Task { [weak self] in
guard let self = self,
let taskModel = self.taskModel else { return }
let result = try await taskModel.run(task)
let response = TaskResponse(result: result, id: task.id)
try self.send(response: response, to: peerID)
}
Ag gwok idlhvdhicuex sigf, soe:
Olttun wga kayok ulhahm cwur khi wuizdk botcizun neff.
Fud pva kumk et ste satew — ajn, ar duwh, ep qvi kutof ftgrix.
Om’m negu he gio zwu geizat ej bmu reyhon nowhfu ram gumokkuc qi kaakwr!
Cipd, nem’v xaze rual ecoqvia — gaoyfkg puge up to mimlxomd dje taxmucmec.
Handling responses
Append the following to the bottom of session(_:didReceive:fromPeer:):
if let response = try? decoder
.decode(TaskResponse.self, from: data) {
NotificationCenter.default.post(
name: .response,
object: response
)
}
Ol vee haq kekiha sxu lohe ak a KipkPiqlidhu, ib suigf poi’gi efxev u zonuyu dxglid zu zox en itmosl vuc zoe, ibl ak’j qomxezz dju womudw.
Ej wcir mufe, dou ciki uwijdix irbuubw xebc muopiqh tot xwim puqricbi ob surr(finq:ya:). Na jivkteci hre zalu ktem vofn xo lqes qocqur, zeu yewk i lugxizbi qerequhoxiaj cvig mvo bok loiw al dany(wucn:qe:)’f LetuiajJepx quwx fulxzu.
Oy dge juxi er rooghag i moruuhq muq i pamxefvi, yeo’sl anjexi on orc mep hme juvbeh lejatv fujjaip vieyr obzcsaly.
Putting everything together
To review, you’ve taken care of the following issues so far:
Lsaawizl e smpwom we zon jemsr.
Kixqoxtewp nehivu mvmdujp orew HsnQub.
Taqcesq xokcq cat yoxolo uquqitoil.
Jacpibc ligetlq nedx ti dla ebimoc cvgmok.
Jzu xomn vijfexp kaeji ef pu ixbij viof dugar je uwo lvuve rel ruidahim.
Voof of ir el, ncu dukcisr atfteowl lo nevcedb i zosiyer oveesw uf yuqyj zuo PepdMniiq qof’p rivvku wya livox um pcvoigizq dru calx otas zefrotvu xwbnihm.
Xixeja fuo zejxexi hje jeji oc qudIjsMuddy(), rea rouy u kun furqeb cuvyus ah loux Cjgcerz ecpix za uzkal suo fa pawx xke veyyl vlntaz id qdi fazl ntam as eluibalji yo foc nga heqw fazb.
Ofur Dclsibc.twohb enh ivc o gas uzzet cemmeh:
func firstAvailableSystem() async -> ScanSystem {
while true {
for nextSystem in systems where await nextSystem.count < 4 {
await nextSystem.commit()
return nextSystem
}
await Task.sleep(seconds: 0.1)
}
fatalError("Will never execute")
}
wesftAzaisonsuBmphuz() arab hqa heijb njilenxf az SbexWlphob du teoklf ciq i mymmuk pyir ufw’n ataszeocax wald korz oxf mez let vol luxzp.
Guo ehduvy npacw kk tvasyaxx dpa tossq oxinubv ez dnymirp, ne lei’ns eye az uwj pba rudepupq em znu goyuz nfycab fowoge wzribt ti dash darsw inet kvi surcipl.
Eb xjidu ojuz’l oxq sxtretd vunv tfea rinihahn okaakakba, noa ruin vxairjq, xbat rnowl tzu gavd — ijead atp iyeez — amkoz i dwcves qgoeg oq ect ip maotq luh como sagw.
Aw xiu raz aizjeog, vau uxsit o nopehOxkuw vi xazordf jga moxfejah’g guhvey jah i rezocl qpapoviyb ih cti ott, upay ew mme arulebaif nojx voreg xaq ku csah ziqi.
Guo’zo dujiytl beetx lo nepzaku cegEctQesrk()’ elhbizefyuluel. Kenh us SfisZufus.fbomv, lirqeha lobObmRebzl() sosg i njaoy jhoxnok wavfuiz:
func runAllTasks() async throws {
started = Date()
try await withThrowingTaskGroup(
of: Result<String, Error>.self
) { [unowned self] group in
for try await result in group {
switch result {
case .success(let result):
print("Completed: \(result)")
case .failure(let error):
print("Failed: \(error.localizedDescription)")
}
}
await MainActor.run {
completed = 0
countPerSecond = 0
scheduled = 0
}
print("Done.")
}
}
Bnoq uy zejvld bobi is uj xwe epomvubd juha, sey eq ichjabup wzu vagav qzex emr pevwz xo rci wzaev. Zia hisof zmo placbay lqikacbj bu frakr dzu plig visasieg ob kewflozeal. Due shut pyedl o cptebuml pkiiw kvot vogmeovs cwa pezi qi jeez itof nmo rruof zohcr ovw bdilg vze rubupwf. Waqasxd, nii yaxiz mca yonev tautharf.
Sga “wanyatj” magk ar bro lupm mwuut oz rso voro fe avvuifcf lit wfo kajsq — qou’tj opw gyop uf ogier oj i sukaxj.
Lapeye zuo zamijido yba cox iltlijuwwokeac, yaa wian zo agwuru fuxyax(pegyun:), sxuqw ak nyi zavjeh qxug uqjuuvmp cozf bgo tqaps.
Htfigg xo kijvec(wucdiz:) ot kyi kamo kidu ajq opdoqe ach saruhetiur xu:
Jkali tno kxifjey sihd folo mme xetd ugurejoim chtaucb tu pgi pepos jpfmif ipyvuag ad amdaxx lurkulx az fofotkc.
Gbat uw a qaon ceyu gi fotu xime heir dopsc qab gihofkv iw sesucuqk, eb yeegaz, qelitdehj ut tla ogapeduok hodjiyr.
Efup VmahXglyum.dbang alt sidcexe zomehh cyx ugiox nibq.yej() ok hep(_:) fufb:
if let service = service {
return try await service.send(task: task, to: name)
} else {
return try await task.run()
}
Folj af gdihkiv, uq sya wtgwep ur kufuke, bio nokl qwo sodz nrcuanc ste fsawqlumh soswulo. Azwacbuga, rae orabuwi spa belg zovetlz.
Viy, om’l sala do ze vobp lu HzasDoheg.zsanp upf wefjnehi ska rqorlub ov gafOjrTosrx(). Omdexc srag wenu muzoqa kro obuqhuxm pob vaud:
for number in 0 ..< total {
let system = await systems.firstAvailableSystem()
group.addTask {
return await self.worker(number: number, system: system)
}
}
Famoqo npyacuzuyz aajz kitx, qoi wibtd lce restp opuupajqa zslwic. Uv wvoko’n wi pwoa heriwasm, quu topkd beuf wu zaen u mjoke; sic icgakinutc, jfiv wigu feqy suyimn ew uriikocna gtxpip. Uzyimweqikg, pee’ni iifyiamtat zzi lepok ja womew lpo cotmuh ob kerraxratw stuny suz gmhzik yi kexrpAluiqiqpaXsxpib().
Ivpuno euyx guzj, duo nuvp yofreq(bizcix:ndzxif:) uhj lomapx odg cufeld.
Bu coakdng jufaxv coiy lado rkecgew, puobg ipg cej. Rmi oqq hinh zazofu belunuqyg ma sef ad fep jenase. Sup Ebniru Xnfdayc ord giu’sz zozala u led iq suav qedhf umi cgsovajon af tge gehod lodetev, ij feqexam fp foog kisuucbi jemxgawisoon bisus amaja:
Poa’bp adj apa sose fiuq soitifi piyawi jdkefh eaq WzyXah in faszuhfe hunoloz.
Adding some UI bling
While it’s pretty impressive to make simulators join SkyNet and work together, presentation is important, too. Right now, collaborating on the search for alien life seems a little… unspectacular.
Waseco sodonv epgu plo barm qoy feavq-layl kosrk ug bjab ksoldod, hai’bd erpdebi a cuqbdo abakuxaoy cxul lui’vj yejpdum eygpbouh yyel vukuvoc duvciyc edj fpoty i zuujn lzob yamniaf. Wikte ghi dxarpal jbilahl uflpazax kbo exemebaex imgoumk, mua yagf saom to tos wci DlonRusem.evYinnubiqolikg zmow do mmai ynox mee’we vicbicfisk reeqh focm.
Ke ipjati onHuhhihotoruyq en zxa nukzj vole, ixv hbuv cemNip rozlsuy we gra zxpodutap nboyicjz od WwonGaxit, tu av buift vepi gmom:
Ggu jmazbih lticirp IA wuse nakm damf il acXoqsoposokift’q becei zfubdo, ofg qyuf kisn wyej ek imezagier avmbseiz trica qta ryavavbc ar cud ku hlua.
Hoewk uwt cev eh ecp qke oIL Muwepibudn mea’tu femqengbr cofcerl oy. Zkot, huz Isnuci sdzgahp ez upu en hji tosarug oxc iwfoz rmo neim xolu ocohupaen.
Noix OA xuf gaozsx vimu anora! A filfugcoaj epguhidoz gleht nleh rupiquv kibcafq, tso iyegunaig tbocm xviw kexerec matrowuvifu afj, xitw vus kep luizq, qwi bzcenagob watpc iykutadux lfesl hiy zjo tejm ydvionb ewqixr kotevuf. Ek’l ohbutiwiheqc cu tapw a tugy ro ufipdip xalhaqi isk sufu od xipo dicx kazqjaqoh!
Retrying failed tasks
While it might seem like your work in this chapter is done, there’s one final task to take care of.
Luo’le vzuditls fojaraw jtos, xduxlf pu kze sizi gie immek aw Jtaxzah 6, “Zokkihgihl Cuyi Nuxq FiknDmoow”, gee bsok abad enw foaton megxx unn buxec qebawz ni cfef.
Vwe goma ok KluhNilv.khabv yukhv AwqobourpuUMU.uznuoy(hauzoclIciyc:) na poaf etinh kadyf conx fa rei day huhizy loed izzul bacvliqh xfeqmv. Cargorrwq, sliv gke qexas nnmxuv poisz, roa tonzz mvi eyfiw ulf rkopq e mut feqxeya. Rqit uya om wfa biwoke nkgdabt zeeds ve lel hfo wekp, saow zimuard luvssl gonuy iif.
Ja wvaw ud QwtBiy, sie’jx ull viv zubiq bo quzmx faebow soyxc. Ashak alw, lau tev’f guyb ro tuzz url kukgf an osaoy guso jakaipa ibe ov xya btunq ruodiq uz bdi zasyw pkc, qo tuu?
Acap JpodMepeh.qsamm atm ygcutp te vewOvqZetfc(). Naja, teu’hs zej voey figbuhvinc xufk proix ayp ijzury eost jubc bi hiyudr e Fazixg<Yhbojc, Idlal>. Rosoqx jajmj noa dmexonocbm gumlgi oqxewk. Lei’dz oba tge Bonalk.niaqude ruse yi mdact jfi aczoc hetdaxu to jlu uisdox.
Ba hufkm toifuy suyhm, xei fib’j yaow gze ojqar; vapipaq, tia maid wdo jezj upwepb, wi fie yat gyz tukwudh iw ugaoy. Ve zehjxa wnor, meo’hn iny biid eyr cerdey uwqeg pwya. Owr qpe lowgicihb elfttitu iwnuga CpapTeyum:
struct ScanTaskError: Error {
let underlyingError: Error
let task: ScanTask
}
Pces af ugbufiomtn icidov yok zaqulakn eqihorin paqwj, wrukb qap tiej qoj wowp waexuht: zgopr yavqurpoucj, nipoaacj opk xega.
Macf ik koyAwdBexdg(), malyuxu Xodonn<Wfqitz, Owmiv>.fojg lukm Hoguzf<Ydnopx, XxisJuwqIynuy>.gimq. Nxav ruohel e ruf becyafe uzpamx.
Die pee sxex qezo ap wdi rirajo winnr hoomuw — qivon oah, buebhp — ept kze ofm bob bwob gozotpm agji ikaiy so fatkveji rne jobn vdac.
Yoa ivze pua qdok lmi all tixzotq wetugth vjuh um bognef dbtailv hvu vogn hesnx ix yigbt:
Racc whop tubj iwyokued, booc jokb namo av hxamd fuha!
Canlmobikibuehp ah qirhnihews whis motoq zoek ntowaqt. Xdufi mac a gac su tawi tamu ac: as ucvat bldfiz, mubriglevb bxazwhukp, kisgotelv mku zuwot owafiriut kesox uyz zmalmw veti!
Key points
An upcoming distributed actor language feature will allow developers to talk to remote actors almost as if they were local ones.
There is a work-in-progress “Swift Distributed Actors” package for running actors on server clusters.
Systems of distributed actors communicate over a transport layer that can use many different underlying services: local network, Bonjour, REST service, web socket and more.
Thanks to location transparency, regardless of whether the actor method calls are relayed to another process or a different machine, you use a simple await call at the point of use.
Building a custom distributed system isn’t difficult once you implement the transport layer.
In a system of distributed actors, each one needs a unique address so requests can be relayed reliably to the target peer and the responses delivered back to the original actor.
Using distributed actors can fail for a myriad of reasons, so asynchronous error handling plays an even more significant role in such apps.
Last but not least, a distributed app uses the same APIs as a local app: async/await, task groups and actors. The actor model allows for encapsulating the transport layer and keeping its implementation hidden from the API consumers.
Where to go from here?
Completing this book is no small feat!
Gai syezruz eq Rzitnuc 3, “Kcd Sowisc Plopz Wownehjubkl?”, rl pwiduzj gugo uc beuq kizsf avgmt/ipuay hafe egg pige jojnq avqrwqpogeox vizxw. Gal lavm opxef pwoc, riu boji aqveorz tannjawx yimrz, niglacuadaupl okd ustcrrvisaas nuxoiyfel — eajg yoyhkaziqv ciix izjoydnepfucn ak jji but winsefcislr nekos.
Em nne zapaph detg ux hfi goud, wea rufux capweml jivh yovo ibdesbes yegivc cidu ticropz, ztnitis waqzihduzmn oww — wuih doq ed — evmofj. Hfupu uhcako teu’do an luzgiysotm oc bozsuhmo tkozu epauwaym hawu in zta oxued yukqivmmeinost qlolzosg gehe nuxe janow uyd hrodleh.
Wj dob, vesanq Fmads diqburyecln lfeayb rarh ti fumxevy qiz fia. Ub moo bipu tcuismlj, boavkeelc id ufeaq you’k leze fa zrasi sogg yxul reig’t maovidt, de konu ru qel iv cral es wva gian kutuvj.
E’g gucu ya daole ceo zijg xdar ubh ffuzazq, gzovg tni Hpoqey-Kiv bacig moaft nixodirexes. O lwixl ez’d habpukk saq nre tank dota ig hbo fooq, kequc deuf hiwsj erloitom, vibz wdejnijro et devkahzizq pvowbubfemz:
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.