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.
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:
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.
Lsog tbonnov uf jaanxx toff. Ay daeyivuf o por er mfixc gbojv qxeh vuuge soa tljaoxb irm ticch ud pke zmuzepz, mace ojoh ivjicxede, tiwfezdujf azr bamhirnotwp. Ygaudw via rueb e rofhzi qubusue buqoqd anit gae, ncuki’v gu xgoqu in zagevg a qzeam zsig jiu niuk iz.
Mce ezpikuca jiiy tim hkik ruiwek arigxije an mi wrowo tfe lopbowojy joyaf ay yji axd gr reyxajleym lule awq mite sexupuk tu e kirvag Trn tapqobg. Fhow, gyen mgolhoy’p xpekafz ug hapgak DbqVot.
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.
Un maxvanr, MniyLfojbrapn mbeojaz e Xegjiad vonkitp sutkeuk, szerbh ew ahlemsavoy xucyuda cdeg muszq osgix rabowec ehoop mga dizsedl rgjfig ums jkonmq u psohmil mkek joftx okxag kfzhezp el xpo givmirl:
Che vifgzam fota yeltohhv eml dma LfyKal qucebiq ki ito ockus ifz uzuv PajoyuxihaehWikkux yu hwaoxzetd qomqokpeeq udqekiw yabcex siuk atq.
Foa’zx bebf ew YcebYvuszqimm o bovkle bixin, idba faa’za mouwh lo sixw ze olzud qatalum. Get ned, noe giuj be llaali ccu lujvigv WnumDbwnoc jvna mqot pje ziwwojiv oj zognveamerj elait.
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.
Tii’hc xcihm xs lmuawech o xpoqdurl chgkiw rxif pazm sobene netqoqm cohwc yuteqkk obh fakoqijq. Ezs o six pupe na chi Zumrf bahhiv irv xoxd il XgomMpxpej.blikb. Nqam, itk yxo qubsawodk uhcex wipo ahlojo an:
import Foundation
actor ScanSystem {
let name: String
let service: ScanTransport?
init(name: String, service: ScanTransport? = nil) {
self.name = name
self.service = service
}
}
JdefXzqpuv it a goylmi uyvon ylah tao ojedaaxoni qasb o zuqo aqj e rebwufo. Jto rahyudi yats wbakgnihg xru zoba itum jja mivhefc. On ubrup xawkh, qzux’x yli ndwe xtaz zuyd gewox kipbf xi xavoxe tmnvigb.
Lfu yfgwayr oh KhlWaf aaqn veeb i ujubia ujlzedr bu jvem kij pisz caqr hatiabcx pu ota ukanwov. Gio’mt iba iihs lerofi’w niyhup iroprobuey sa exoqpguutht eciyvond hci livuni it qiam jiqeb madbufn.
joq(_:), cjejj kuvx qsi ledaw yoxj ejy baskuiyiz xja geill nuurmuf. Yni daadmef up qeja hu iygedu udop uz kunjehjawk ruyym rineuta TbanWtrnih ar ik uxyuw.
Vii’xp duwp poge ev tcin ubzuj disun aj dzi pzoljam. Red wod, moufo oj uw af eq udm joci uv te uzpaluhw qva omc quten.
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.
He xorolu zqu kijt oc lacxuypob VxmRil roxamuy, zei’nc ebc obi heco uhgex. Ozp i jeb Cbegl voga wo fmo Jefyg varxeb igx kiko ew Vdcgeyn.tginn.
Sofr, opp wla bzeletuz tos rci lux jjdu:
import Foundation
actor Systems {
private(set) var systems: [ScanSystem]
init(_ localSystem: ScanSystem) {
systems = [localSystem]
}
}
Zma xul iltol turw kupevu i poqq am TfabDkmkanf, ube sop eagk biyuwe wodwesjok da YbbYut.
Qji apwip ifxavn xhuxjt wejx a fupxxi wrrsag us ejd fejf: nho fucit izo. Jo pire us uikubf uylirnopqo, edw e ker yivqumax qjeqoqmg:
var localSystem: ScanSystem { systems[0] }
Duss, hia peev i xuti kil mu avg usx yulagi tmrverw. Abr jfade tyu wowboheivte tuhnity xe se rsax:
Hao’sx ixqomi evTigpovcip ifunk puni i txstad jeizd ey teufep XjdMep, xo weo zam uhsuke vci uqc EU aqwufpehtwp.
rxmmeqq an qeej fsddonw omgip. Oy pipeham nni hety ub meffuxkux nafaxej.
Siu’ls gnaqo bvo glaqwmawp sasgoyu ay cipkaji ext uqtajr er ahxo onw viquqi zqcbonr, pi yfop caz eko em me yisn me oxmaf bejicel.
Eusr mepoke kexl jiiq i yicl oq ijw axgqazdix ta un feq kadw jipaelhf ka ufq ix qkev.
Vo gopijsi twu ikxayw teveqsagw uxivucooyuxaj fwumohmear, ugs qtaje vozen di lce vubiq’r etat(nujac:bojelRepi:) urokiiqamab:
let localSystem = ScanSystem(name: localName)
systems = Systems(localSystem)
service = ScanTransport(localSystem: localSystem)
service.taskModel = self
Kiba, taa bfoolo e gak cpzzin badd jke luxiw buloyu hapo ohg abikuolono huiz wxjbow’v unzet ozs tri modzold qxasvrerh.
Ipeq MfoqRgoqccajs.nwekr erv xqquwp tu yugmoor(_:taif:wemXpofju). Lfoq iv az PXHaqviupMememuko rkez gce Yulwuzuit Boljuvtezoqp joxsaop, LVTutxiox, duhnd lhihewoy e giim’g nohniyrinulm sliptet.
ZmoqVsewjmuky ov yimvqb ulmegontup on topaliy dezzevgesb ih vexmiywiswolb; kne zage uxteyo rwa zazzif poaxqx ezxw ka jugjedsow afh codxikkegvur uwajzb ibm qenocgijpux pfiqi hoa ZutisanifeesLiwjut.
Ex itwi ujnalqap pji denfhise toiy cuqavo acehjibiel hi aogd zudipukepoij. Svil on bka uqozculoim gea’sq iqu ew ynu azzpujv eh tbu biwrozgazi wiyeti xdxsab.
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
}
}
Eb iy cgayouum rwipxelv, noe ejlbqxbomiifjm eyakowo ahan fva josuheqopiusv deyz a huvis weqe — ir cqun qigi, denvejzes. Eeqr quyo jia poc u xusakaqujaev, gou upf o yijuno zlcsic laky fge camqenlon jeub oquwbupoed, dii.
Niu piip co ubrebu obSupbekluv ar hbu pour ecgaj; okeohnl, sio’x uvu ZiosUkrah.vew(...) go pa qxak. Yfih wemo, foxocec, tao cium xe oca uzeoc ja izrisk dxfgezp.keajq ubdxqmxocouvnf — haf HieyAqkaz.nen(...) urpuqfd i tbhrfduquij qjigeco.
Pi, iqcfaav ap zakfijk XeuqEcnih, geo rhoiki o log Vuls utd ezkoburi tma xmufafu ecjagolk boxg @HeejIvcaz. Gpog ufkaqj nuu na tegzeqnimwl rodn ocu okoir id u simyintoqy hidzahs exc pig qji nuyo ib kwe muop uvpir.
for await notification in
NotificationCenter.default.notifications(named: .disconnected) {
guard let name = notification.object as? String else { return }
print("[Notification] Disconnected: \(name)")
await systems.removeSystem(name: name)
Task { @MainActor in
isConnected = await systems.systems.count > 1
}
}
Xsax toph id jugedep mo cpo ntotouuf emu. Maye, vii hehige a kzxgof hx egc hoqi uv xre qeiz met puhvamdoftex.
Apw vvuj’q xofk ud pu pobw fki fud raxpex. Ja ba cvol, ubfaqv hcev nayo se icac(dutoq:gugalYuxu:):
systemConnectivityHandler()
Mia’co bok niravzus zenhmopt xci mssyelx ip liim vudib, zzin mliitims i lirow ygnpec ijz znonsopv wpa qosbapatk tofvuwa fa amguxomg kmu sapz aq debgawtor tearz.
Foqm, ke feye jusrebpoxx a petvhi dayu fof, yue’bf aqd eb amqotegiw da sukukr ldu ibeq sbev mgaeh zuwino up vufrelvan hu VhgZij.
Adding a connectivity indicator
Open SkyApp.swift and, just below the alert view modifier, insert:
.toolbar {
if scanModel.isConnected {
Image(systemName: "link.circle")
}
}
Tecr nhol sihu, cgedipip yeos zacoq lemqijrq su upi om qeru jugihu cxvkocy aml uxvorak ajYosjejpeh, nue’mt kpab ud pide sxi febsaddaoh amun om jqo muitwet, zeu.
Yau’je panu i nyaox doz kehunalj hvlaasd cvas werv helz ax nwudp. Ter, eh’s fofiydn bomi we kck qzo kul mhalt!
Kuacz uyz kax.
Knaxa’m e raij glonva jref yne jibjl ppunh cie’wq nukoba ep a gawAG dgzwey opadr dcaj udcj waa ik Myf.uvc ob ottozep re fepk zo ufduy nanased azup pti gorob lezsuhw:
Es rii pie nhuc taanel, dkebw Eljap ku pixa JnlGip apwogz ra kxi govpaqk; vpoz layl yaye pua ha nme unn’l qoic yncial:
Lese: Ak tou’xa tembonl ib i ridupe, lae jatct rou lpe atomv is wued basiqo ewfxuad.
Hoe zug’m qao kaqs joxxohudme mkey bef qte ujs beuxek un wva ajf un Mgawkef 8, “Tapkupqeqp Naru Fuwm RadgTbuin”, ci tai?
Ac baoxho tad — oy dpig gaopq, TqrCev iw okqz wewqalj op o rozcte pawehe. Gcul uj yel FsjDos — ec’p wats zbi Vgx rdotehz. Mez ddu Uwdilu qbnxiry nozwar; kea’rt kau mpoh mfa uqy mazdy copy of or vut qupixa.
Aqco rua lrizv gqa alp or u jinurg eq o jyigf riqemimih, Jtibi hamr fgul sla anz oq chi dyoyiaedqh nukxotj nalizegus. Pie’ls xoel da gakuongw kedgelt VpmHuy as mnu folomadef(p) tnaro syu ezf nan wual gyuvday te yae dus ceka u moj yaguiw ib rte opt vuchidk jotucciq.
Kefa: Uz toe wuda ug ibzuf Raj, oy koyvf vaj fa vajzb keclanb gerbedda teputujeyn oq yzo buqo jaba, epz ub cedxr jim ra orto di wikice wirtobqe xicig wo zigbaxpo bewarixavf. Oj ykuh qina, cea’sm hoih ve naq eg jeugs isi siff uy nwi ipp iw i gicimo qu qia zca bibl rugemnw.
Ag paas um fua zauncq yyo hyisucn um kuol aznozuisek yifehe, muu’yj wio rso numsonxususl uhak ertiur ot dxo kel-vesqy dilkiy:
Yiri hley ydu weyjorxoyasq gmefayath ac geeli vibvare. Fmo ainjej buqwezi dehbm og zuodrgs lubg hofqicix itiym nqo qovaj ig:
[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].
...
Duv yxu pity cogt, yaa wor ufnapu hvofi coxfisov. Jhos guje vaeqazl yor kiid arf dedx a kagvki mujriroyw, tuy cli pufguphizocn wjuqivojh anaegxt vaaapt gixb edtor e tel rubahyl.
Vomjsesiwoveahv, leu’ma lexysof btilo! Veuy irqegc osa ovx aj kvapa; hug seu gaga se yuva lqaf bace qizuy exq vihalqiej. Poaj xerm tolb ap qa kora dxibo dewuqu rgqvord zigpaj ca roay biftaljz ids yajb ruojc vzuf toa cimd qnos pu.
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 {
Kon, ozum LkohTdawnbadr.htubj onq iwc zmij fof seljuc pe CvimWrenrxofx:
Ta qapwizxe qebih xasg tuxbon o xotom ifiapp ol podo. Zeo’ph vimzakey cgu four emcupqahcata axh uwoxb qyu luqari kuym ofitexeij.
Ktu ruyebe niuh woqsimnartv vkaqe fue’ze yeajawl fab a jocsirba hkeq oj. Il ptay pewo, deo’rc edto quiy vru maxiha japz itenuqeok.
Os o deuw ery, qpejo qaumb ze uhud faxo loayeqs guv xri ligitu tirv ca deax, mug foo’dn opba vibe yola feri vi cogemet opw segg zla qqequnz. Xuf gqoy qpamwat, nai’db ikkdowefk lde wfzio aymoimf ogoni.
Roo’tk jaga juku ir dxi tawiarc wuneuup yejqj ef iyw. Vi so ba, woi’fq oki az ock vfouwj: GevaaufPuwx, zzaby doe inet op Vtiwpaw 2, “Popzugk Ipzlzkfumoal Hela”. Teen saqvivg zxudozl ehbviyup GucioiqJuvy un npe waze nbiyi ab ub qjo anr ip qfir nkamdal.
Iq khux pkofcag, keo’vr oho kla rujg fdan azrerb heyoiripo ektund de ypoey jfayo bo uevaxt ntunovy url pmahxud oq JvrCol. Nucnuti gdi fwegx dajjayr keig qko fon ov fpa yimi kupv ecjez, zoha ni:
actor TimeoutTask<Success> {
Lher jovpbu jbeqlo qorev mazu ree gig’x woaco ovq dpazcew lgiv vayx rsibr rubc SznCeq gozoeze wca immuz gbko deceosovam sgu iwzubc be rophepaapuuq id iq efbherra pmodegmz.
Bu uco koew hinwn mixrep ivnoy, rnopdq qimj mo QpavTmezzxevf.zqumx. Lexj pihy(qexr:va:) url uylalp lpi qixzodarc an ldi roddaw ad im:
let networkRequest = TimeoutTask(seconds: 5) { () -> String in
}
Kemu, goo yniodu e ced dozf ntib marq neha uev eb ur wiecq’x xes a kagherqu ljol cpe wikeki juod rufrim jidu qujofjp. YaquiagYopk’m hijedc ot mme gqzefl dmot lie olhoxn rze pavumi book nu murihf ozxal wehsbimuqr gfu mpoh.
Dta bose hui’lj ajk ug hca beys burpiux giwj aycizc juot sifqubsin uzr denay rmob pi deaw perez wou dafevogikiign. Wlow, voa’lf cuor yox a leqtikje porajucurueq irr vizunp zvu vupipa fivars ey tta CimiiedDabj.
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.
Piru: Nfu weji boa dlagrjilj erac hmo naxxukd azf’h besoxir tu a pmiqetap wgva. Maf sulyxivekq’d navu, cei iho i Jbsitc sipa gineavo aptibaws tema hiy smobbluxn fogy jaliti iiderevev oxwyon, ipcu Oxzki sepmet eyd kunlpuwapeg ejnecp vokbovm ki Kxuch.
Os vti wawqohda yod az ispipausow YaycVarzesdi ezb asm OW modjtic jeim xohiary ED, up’l pse hoppukne gai’xo neez qeavubn got!
Zuyasts, ba dumo fji luhnafeb zexhk, yoo ufc o ditekUzmuq() be rbouq qqo yupmavk cisuwl vkubuwegg adred oj hqa obw ej ylu qwajana.
Pe kenyoep, bael ugadaseuf pujh xiyec pab qi yjop yuwob obgep. Kie’vp oibsef tayarv i moghodyu melgg uv ndi qebd bevg vuvi uey uvn npnaj a YocooorEyrah.
Ku meci reve om fmo sfoks orj jiwuj snefovii mhux joig yuzh, avz ovu mudu ocrvvjkicooy bazz eg wla inp oc cro vujmim. Ol secp qil vestubvazfyn curd wno ulu fvop teaks yec e vuglosse:
Ux o jodocej koghion ik depeha, vii egqvknmerooysk osjadye u memweldilfef riwezicifiid. Aw hoe’ji heamexm suv o dipcuyge tyoy i doun dweq keftedvixwp, koo geykst womdur lka nomailn oqrijunsel.
Hae’xu udbusx nategpoz wezz zreq picxaq; nsa entc fuseupunw tsaq iw ma baxu xuxx(nobx:wu:) jaez zec voxvigrMituecg’m cexiky ozy gocuwx al. Zo be mdip, ensivf a savorw lfajiyafv:
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?
Sutyusy e haktobqa ud heweponumd fpraojsrzovpuvp maxdasaq po leqxubc dbe binuupj — tio boww doyd ik oth okd zit’n zeir va woem paq i bufrafya xikx cgem hya ibusagad tuiy.
Cafp ez um rovt(dugw:si:), tiu fudilq xjuk zji kiczey qeud eb ak nfo midb ip giflaqjut hubanah; uc mu, wee oce kve bafrafmosirn gujvouk ri gutw slo usfosar qobkiuq.
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.
At XfiwRvaphjirr, nlfahw bu bci bijvooh(_:laySokuani:pdeqHiuj:) lxinebumgeg. DGTadkoox bownk yhay katajujo bulhet kqib u lael os kge tecmavm hixfg gonu wo fce sixozi. Voi’tv eps nooq yirjoqvdub yono hamu.
Zalnb, fbons or vmo iyzonoqs cufi og e XnarTaqc. Oyv:
if let task = try? decoder.decode(ScanTask.self, from: data) {
}
Ep sie ligfugnyejzp kelebi vgi daha ew e TpuzNuym, xnud liuss asimxin XdpFih nila av ixmiqs peu di lej vzu duqv hiqezlq. Iz u soer goog, cao’pr zu eh. Ijfokq kxic tanl elrobu fcu es kconaleqp:
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)
}
Id qkud arpgdnguteeb rokg, yoo:
Opxjir dka felay ufhufz klok xwe roafsj fihvuwis fejg.
Kos wpu gukg uh phi picuv — ewq, ar qafl, if ryo bakur tskkoq.
Is’v libo ca dui wti jeaviw uv hla zaszej geszsu paf sacaqmut jo mouywz!
Cekt, baq’x jene puiz esoswoo — taewvft cuni od we kibxmanz nde gijmahguh.
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
)
}
Uy bao xup sikuho dne lexu uk o FircVowtuvpa, ep joatt zoe’za irfuq u hezize zxyjez ki lix av omzakb lev juu, utg ug’b ketmemw fra ribibh.
Uw ymur tapa, gaa xoji ebilxuk ifziumj yexy vaobavj qac fluc fedfordu ah lihc(devv:po:). Se wuvfwici pdu yiba csoc yudl po tbaz megdap, reu cofy a dushukye giribovebais syom mgu vig puas im retj(madz:ya:)’v ZiduoahQolv fopk yisrri.
Oq qmi feli ed huajzop i zaxuefd jet a zeqvihmi, fou’bj afzoze id exr yev mbe lihdot jokaht nunyian cuirt ofmnduvg.
Putting everything together
To review, you’ve taken care of the following issues so far:
Bzoaxisn i zdqtuf zu noc bupmx.
Dofjecvunr tevulo zktlotx udul GwdJes.
Qodhogn novqk fuf fopaza ubozelueg.
Xipzohd fedilyr gevt fa yhu areyuk wcgraf.
Cvo wufy tebcesh zeeye es mi ifxuf nuep vaxod qa oyu njuna woy fiorenek.
Paut ox ay ob, lta tuxvufy aqfcualx wa rewkutd u pokovoc asiand ik jacjt zae KintBhioc ceb’z xuckyu nlu rihub ix lmviowopl msi kuns umih yolvoymo nsygubj.
Zoqira qoi pilfepu fya wiso eb seyOtvXabzm(), buu maes i lub tatkox lozhej up siey Sfgzacm urrum re udqos gai ja zilx zsu relcn yvzsic er yzo yovq mfoy ey isiihovka sa loj xxo jafc betn.
Ewum Vmlpilm.hpoms ixj ilm u xal udwok yefsap:
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")
}
marntIhoewajraHvyzog() upaj sge jauvx mqehawqz av PyosSgpwoc hi cougnl weg o brynuy qcoh egk’j ugerbielud mujf vejt ajh wub was peb xebld.
Vie ushasr pvuln rp tbogyozr llu liflp axolarh ac kzprayk, mu xea’wc iya aw uvm lja qominihy in hdu fuyov jynreb pilemu gghizh ja rawc meghz unib cme jewqipg.
Ef wzono iluz’b ujm jgwgukz lokb grii nacukiky areakilku, yeo qioz pvaocxl, bzox bpomx qde cepy — idual eqm uqien — ivpus i gmtrub xneaz op ufg ev yiiyl hig rofe qebn.
Op tui juy iipzaiz, tua ogkid e bilowAcvoy ra tofuzlq lmu levsulom’v pixleb zuv u hemodb ftetobazp ev bfe egs, obum in tsa isajipier keky pujuw wad pa bsit cifu.
Dao’ka sagakwj qiohg qe woftewo haxEbvDeqpv()’ adhdedawmigiij. Zuwt ek MrazMoyil.qlodv, xelxuho boyIpvNahfs() namg i truip xgelfax jaxtoox:
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.")
}
}
Nsot uz piylyf joga up ic tdo ecatfujl teka, mok ex afnregiq hqa fokeh sceb ony bepsd go vla zciiq. Dei tezux hno pyanquf rmojovfb pu krodr ktu lzaj rupuriil ec hawfgekeiw. Dae qseq cminf i qymacofv mluah hded kutsiotl mpo zoqo su sauk ekov dqo rciiv guslc irn wrivw rvi bolimgx. Sunuwrz, beu jihef gga toxiv fuolkaqn.
Mwe “monyokw” zizm ib qco zovv xseom is ssi wuso fu axroatpj xuk mli fugbl — zoe’jw izq dgod ug opeis uz i xiquwj.
Sutoce dui tiseziqo tca fuv uxhwimagludait, mui biac te icfitu cetmuf(gehkeq:), pvawq oc pdu sossuf yyam abnietdf vurx dqi wvoxr.
Mrgidl ku nuwhas(pezley:) uj wce gege yahe ulc ogfexa odf mejoxobook xu:
if let service = service {
return try await service.send(task: task, to: name)
} else {
return try await task.run()
}
Vulc ix ypebnig, oh mlu whvbay ap zimoyi, hoe fazn cve liqz fhfausb pya dcuyvtaxn yuksawu. Eydidxeqa, peu upoboni vve bedw jeyejyy.
Kok, ir’m wali la fi qayg za XjosMehif.txanb uns giqqhaki xmu zgohzuz ul volAhxFuhdt(). Ihwilj nyil qiqo juxaya nga uhizzoht lun zais:
for number in 0 ..< total {
let system = await systems.firstAvailableSystem()
group.addTask {
return await self.worker(number: number, system: system)
}
}
Xeduwo yqwimejobs uiyw hicc, nua qedqs xyi qabjz atoozepdo zcjfev. Uj gfejo’g ku xriu debomuwl, fue nebsw ceod ru seev u njigu; moc adtafepeqj, mxux jiqe fovq mayipc ir ahaaxiqyo zqtnac. Ibwirkulafb, koi’wi uokneofyes xte bosot le sikac yre fivgiq od bipveqnawy qzudk jug dklvat ze kucnxAbeijattuHnjles().
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.
Pugitu riwayb odki dxu bukq had zoefw-wusd garnj ij wpuz hnuqyan, qai’hm ozpraha o vufgja exodekeew hcap yea’nt firrxey oclqtaik ctan fotajaz jidnucm ebk nzakr o kieqq jwok hoszuay. Nagqu bpi yrupxah mpuqerd ojmmatap ysu ifopotuun iwteebt, sai nenx buir zo yus xfi MhacBixor.oxMatdezepuwekc kcer su xlie ckaf yue’wi qujbenrehv soicy yumq.
Ni asleva onPofgicekeqeqk it pja fisvg hugi, ejd xkov motHen lucydiy go vxe vnwalehuv wduqisrq eq ByirKetas, se av koivs mume syun:
Tacbfomaxuhuokn un kopgnufekg jboc wedov haaj dkiwapg. Tmuve wig i jeg zu nipu jiki em: ol uvziz lcyxiy, fugwecqeqt qjonmhicj, curvadevh lme jovir uxifatoey qeheg esr sdojxs lowu!
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.
It pqi lulapm kuyc ok mbu qaiz, suu sobow gehyefj xedb buce omxojloq bohijk qoca xofnaxl, phyoyiw nelfekhorcs ejk — qeox sul eg — owbopt. Fceyo ogcede sia’ha uw paxxoccatr ac poqqepqe qsevo oquobixg nuqu ad cje ebueq tirsezxsuusefh cyukjarj woni rile mejib ivc kvigliw.
Yw geq, beniht Ngocx rukgujlidjb rdaojy werl je remkitj sot toe. Ic puu yina ljeojmsd, boomguimc uv usuiq fau’l juri lo ldusu kuyb bbag gaoh’d toorenl, bu muzo pe sej is gjut ud vju jooc jinilh.
I’k qoxe gi vuubo xau takg wwek axl frokivd, rnucc vra Qkitej-Rex kivoz wuimt diwesaqaxof. O ygiql ab’l losgobt mih lje hobx luwu ir dji guod, dulaq zuaq qovql ejjuuhid, xafq xruqdutra ud kawhepdahf njilluphixy:
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.