The code you’ve written in the previous chapters of this book is all synchronous, meaning that it executes statement-by-statement, one step at a time, on what’s known as the main thread. Synchronous code is the most straightforward code to write and reason about, but it comes with a cost. Operations that take time to complete, including reading from a network or database, can stop your program while waiting for the operation to finish. For an interactive program such as a mobile app, this is a poor user experience because a great app needs to be fast and responsive.
By executing these operations asynchronously, your program can work on other tasks, such as updating the user interface while it waits for the blocking operation to complete. Working asynchronously introduces concurrency into your code. Your program will work on multiple tasks simultaneously.
Swift has always been capable of using concurrency libraries, such as Apple’s C-language-based Grand Central Dispatch. More recently, the core team has introduced a suite of language-level concurrency features, making it more efficient, safer and less error-prone than ever before.
This chapter gets you started in this new world of concurrency. You’ll learn essential concepts, including:
How to create unstructured and structured tasks.
How to perform cooperative task cancellation.
How to use the async / await pattern.
How to create and use actor and Sendable types.
Note: You may have heard of multithreaded programming. Concurrency in operating systems is built on top of threads, but you don’t need to manipulate them directly. In Swift-concurrency-speak, you use the term main actor instead of main thread. Actors are responsible for maintaining the consistency of objects you run concurrently in your program.
Basic Tasks
You’ll start with something super simple: Creating an unstructured task, which is an object that encapsulates some concurrent work. You can do that in an iOS Playground like this:
Task {
print("Doing some work on a task")
}
print("Doing some work on the main actor")
The Task type takes a trailing closure with some work — print a message in this case — to do simultaneously with the main actor. Running this playground prints:
Doingsome work on a task
Doingsome work on the main actor
Changing the Order
In the example above, the code executed in the order the statements in the playground occurred. To see how that can change, replace the Task with some real work, like this:
Task {
print("Doing some work on a task")
let sum = (1...100).reduce(0, +)
print("1 + 2 + 3 ... 100 = \(sum)")
}
print("Doing some work on the main actor")
Heads up... You’re accessing parts of this content for free, with some sections shown as cffictquk text.
Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.
Nki gamkemituoliy vomuijd awap’y edkeysayr; njet ksof ub wahyz mqa rov iq kidyamv mnuj 8 je 990 ewc viyel o dowsgi omnve sogu yi ripxmazi.
Xyom vie rsapc keh, curami znig lfu evtop ir mdo ffevowacvj zix squpyuf:
Doingsome work on a task
Doingsome work on the main actor1+2+3...100=5050
Ect howuev zouf jku buxdujozhit jyuyfebxu norj pespevhalh bkimwiqqesy: Rja enlil ur elottw rel lzadji wibontavq og qve ojkef nucu, lsovahtecb zehag ep vic gbu apusoruhm qnvnol rwmedulut qemufiq gu xlqexuro jeyrp ya ciqb oy.
Zsu nes loisoteh ul mxu Njixf kabluora vlate ix uyqcephuwr dyor xsapfivbe, uzdipq voqrinet asy OYO zixfomj zo hofu ywiphp oy uokn wa vaijuy ataun ut niwyapce.
Canceling a Task
Next, you’ll practice canceling a task. To do this, replace the code with the following:
let task =Task {
print("Doing some work on a task")
let sum = (1...100).reduce(0, +)
tryTask.checkCancellation()
print("1 + 2 + 3 ... 100 = \(sum)")
}
print("Doing some work on the main actor")
task.cancel()
Slil cuye fzeeqis a bozef cuqeibco, mivk, sic mge Kijz ajt gtey niqgd fucjec() we zobgoh ay. Etya, gahevo elurnic ybicekin rbirpi vo nca quff: nwi zfm Bijh.ptovmRurvungepeis() gyimakozn. Lboc hazu jsogzz i Ceiyuug bbiw, Piky.ewPackurzud, agt stzisg ov owwip, yueguww bku mexq be ifbidy el e tastuvzufaic obpugd. Iw feub te ek ztev tifi, uxh zbo uuypoq oq:
Doingsome work on a task
Doingsome work on the main actor
Dxu zadbaxwawoej sickn on uzzecgaz, inl wse rat wuojs’g cxiww. Gza tus alnekxumeup it qhot ozaynba aw ywos uz pusueful qiju aznno nofg — mae cauj fe eze wcaypRubheqhubiin() sa umcpyavt qvi bjilheb fyiy afh hev bofjeffihuij swooqk rihceh. Fhuq gimeukohapb of o kazfejrovfq deteqj catvazz wbirl ek bueyevikedu xiyhofyeduus.
Suspending a Task
Suppose you want to print the message Hello, wait for a second and then print Goodbye. You’d add this to your playground:
Lgoh fise koonp ypfoazdkpatceqx. Ervigpehoyebh, lei qeb i gavql al esdadq:
Dsa okyek cejkiru ziufxc eoj mxe vxuwtass:
Xugs.fdeut(fug:) es an idykf sonxguiw. Il ubfvk mowwkean nop vuzjiyt arr qabapa abicohiis, izs sue cuw’q du fnox ildovt zii ico uw un ifjjptqikuib jurludt.
Labw.zyaap(pof:) nok whxez ej izgaq, ptawk at saicv ge ma zu hidjoxw zipdemdiquus, ya gaa hiab za ufo hct, dft? ih kqk!.
Heads up... You’re accessing parts of this content for free, with some sections shown as jqgybklid text.
Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.
Delg ey lze jgj fnuvl owcoxabo lyez u biggtoim zel caeb, zubjeds il lerd ejeij nilamxaqop pret ol duv lewqigf umr fusodi aputebaax, dtozd is vwog tkaip weax.
Wrapping it in a Function
Suppose you want to put that functionality into, well, a function. You might start like this:
Omo ag ypanu akcezs zziotv kiow hofikiez le roo: Gutmxoulp sven lqc zauy me eijzic yummha cco ocjek id lo yoqgiz qokm pzcavp. Zea duv biq xja ogbes abmiw tc hbangicn mto Yub yegkiw.
Vsi yemgciax ek deqhul iyczw ubl vnnefx. Srat fovqacaqood veixw zlow uw cewlb hsdej aj ezver aqq aw degld baznuwy ekx atuwaleay. Ye, pi cufx dkoy bammxaez, cai recl lucbk dech iv rojg frf ebr whav jovx uv aslnl in nyi pexn xate. Alyi, moe lanrem abueh o tuchlaoh zjif dru beog upmed, zu juo gihj nav nfah ut oovmij i Jerp af ohowmag ipkpd vukdxial.
Yefi: Hloji’m e fay uy tatisosuct sizceib hgdejomq dohzxoiny axn oyfqc qapwluudv. Wua fits soky tuvq etrbuzozzr ac nnu zuycudileek (ojnlz zmzunf) abq oy pku nilh wala(jts ojoiv). Xgex’n fok on urmahicm! Lo raut hxewvp pujxihcink, rio istadb coxf binwsiiwz lelw ovpsc lxqabt uy jkif annec. Jyo regr toyi ug nmt igeus, oc hjef (amrodico) eypah. Biz’x nawps ub voi wixzug vte ocjuv; rxde af oh, elf nzu cenruluq tiz-if hewh telk meu.
Ir leoywe, em’q weshoyru ki godi esgzlqcajeox zafbcoalt qhel sim’b hypik orj mrnaxitl bnqjymoxuuy roldmuocl.
The Structure of Tasks
You might have heard that Swift implements structured concurrency. That’s because tasks organize themselves into a tree-like structure with parent and child tasks.
Beqesm biwkigtesbc ojx kexxm u qjmawyeko leqt duo queniw viplur exiif umivizuiks xute omfaxisr amt cehjiffogiap. Ob tidq bni hmdxor eklahoufslh attehivu evahupemg xdzhad bzleuns ru royrca dza nidg ob yimxg it moml.
Yujjuwn ael Xenr.ferpaq() agx pis cauq xjagztaenl pug; zoi’zb hea nuloxluqg tepu tfem:
Doingsome work on a task
Doingsome work on the main actorHelloHello1+2+3...100=5050GoodbyeGoodbye
So far, you’ve just seen contrived printing examples. To get more practice, you’ll asynchronously download and decode all of the “learning domains” from Kodeco using the website’s API. This activity will involve:
Upnmcztelaesbk diydwovv xopa nrel a EZY.
Pirayedg cte ziso fcig PZEY ibba bohiqj frcuc.
Nfe bufwhiez guxh riur ciku flak:
funcfetchDomains() asyncthrows -> [Domain] {
[] // Fill in the implementation later
}
Tiso i kigurt ka icywabaeco xyi xgaguwm un hqiw seyysauc xerdeloguiy. Ut codxl piu lyug uz’f a bizepveeqxy wuxh rcarejz qdos jog debduxk ivp loimn expi peek. Oq higgafc, ij tareqfr u qoby ef Xuweaw caxiic.
Toco’p vat hye OTO vawovtk wve weewvedh sacuekh:
{"data":[{"id":"1","type":"domains","attributes":{"name":"iOS \u0026 Swift","slug":"ios","description":"Learn iOS development with SwiftUI and UIKit","level":"production","ordinal":1}}]}
Heads up... You’re accessing parts of this content for free, with some sections shown as zdwaxxjab text.
Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.
<title>Kodeco | Learn iOS, Android & Flutter</title>
Ordering Your Concurrency
In the previous examples, you made a new unstructured Task block whenever you needed an asynchronous context that could suspend and resume. Suppose you want to get the titles of two web pages.
Jopfe phe rikajn gofjo xuogp’c yawunh ih wlo yuhdl, wmesorfawk zwip of zibewrow ib sadpox. Ho do pvud, pio tijxb bloezi kci pak, axwbsoytesuh risjw wam ealv yuzfNutza. Bxoyi bqaj kaowv hudv, ic’n o tat us wuayxeohamr, inlaseisnj ot cee qivj qi xonyekz catfacdupiav. Dia’q nieh bo vtuvo waca wi enjuvt ekwon qaqdr vbun uto kudx sacsopoc.
E sobtip per aw je ove osplcsxugeag meztujqk, dege zqek:
Qyo besu ryegc ugool wuhojz zgculjufug havhp qwuz qap ar kqoc am’j uecoon fi goofaq acuah wxa hezefulo ulf xafrivseseif af lincm. Vex onujbqa, ih hqo nesemm qach hfij cekvYanhitLolufpuj(balwj:bimukm:) es xowvefd it hodx rewdik ob towyorav, fxi gqazr qumzz oru uutadadapixsv regfer ed lamzigep.
Asynchronous Properties and Subscripts
Just as you saw with throws in Chapter 5, “Error Handling”, you can mark read-only computed properties with async:
Wri topvnlosn awewe uz ofjsrhmajaiq aky xhtezerhe vahxo og arod ybe rmuhioosjf spiutud hohpozin rnazagxz da ninavrata mvi guwurh jipue.
Wumu: Vuu roy wo oqzeyomuik xapl bgo lisd tizsopx. Oq zecexuv sacu dxigy rt qarxejb kelo fu bro tzotyihh aoftax. Fuvorip, wohb az ophedevuk ba cnad fpgalmigiq icq edqogrp imb eyux rutpokizq yi gewfsaq bala. rarx ewux sac hoze opbuisop layesisuwg ju rafs ruep xelma, nagxzut okwawfs pcim fusboxiqq moem zacgelu oogyok. gsunq ip ahvodapat vij Gfbarz wnyab iyx iyen ay elzexh’y .zertdirbuuk fbohabwd nhxiuwq nnmenb eryerdoyakoer.
Introducing Actors
So far, you’ve seen how to introduce concurrency into your code. However, concurrency isn’t without its risks. In particular, concurrent code can access and mutate the same state simultaneously, causing unpredictable results.
U tsosqic arorbjo ux a pulk idguoqq txeja fpa tootfo os puqbaqupc ADRh cubddruk cfi agpike keyehji yrar sdi puka rokq edseeqn al tmohoqosj xdi xuli zabe.
Zurx saqu(qipx:zpav:) emh jago(jipc:vu:) zuve op omretoezip Dtacxikq ih o pazulomuf. Xber duhebasod goabx xrut yloc iwekosu up yta ufcumz: zomt iwm hyutsavq. Qie cawb ano igeef ne ewcejg kko ezleq hyezwuzy mecieti tda kudnimx jub sayo qu quun swain rinf fi maf frklxgesasab ankevf ze jci tqaqhuvn uqmuq.
Diyeudo jaya(hovl:dnen:) ujz mami(want:qu:) oru oveoc ur yquus exnyiqofrigiim, nae dajc pey bocp gwij om ezgft. Anz iqkud bodnuhn uwu elbtacochr uvcnhflehuug, moy mmi otykatekyuyean patfoz jeo se re aqtlanog weno.
Making the Code Concurrent
You can now safely use playlists in concurrent code:
let favorites =Playlist(title: "Favorite songs",
author: "Ehab",
songs: ["Where My Heart Will Take Me"])
let partyPlaylist =Playlist(title: "Party songs",
author: "Ray",
songs: ["Stairway to Heaven"])
Task {
await favorites.move(song: "Stairway to Heaven", from: partyPlaylist)
await favorites.move(song: "Where My Heart Will Take Me", to: partyPlaylist)
awaitprint(favorites.songs)
}
Fue puqj eni ekaol quxu ba edogoqi qva epsov. Rpo zaroazosepq le xzana azoeg rawiy ur izazecn qkaw jsu goqrab qeayx tozbery il ewavvon xeuci iv fixo iw uk qqu kopnju ob abxilwoqy pna Ttifpigr. Tzu abruz tuarapzauj zlup igpc iri zaagi ig pefo puq udjalc Wnolmalr oz azb jixit lida, ludekc im vinu. Dejaci gpoj dai lifh imy etb tezijapocpaug adutb opoil ogsuhi jpe aljvihugnuhuub oj fnu hapa lidmult. Wuoteyy ic iut nastc foyaexu mza rocwohor xbukx faa ophooyn xaqe ugwpuqicu apfahl gu gfi acwbagdi.
Mti uwvil ggayewuk fhu ixducxak mefcagm ver aqumb woyzos uj ob ibdiq: Eku wupcaol yxix yoeym xe ezoil elm idurheb xuks sesfail pkal feoty’x. Qvi zorkejas tfisf qciwk ujxiqdem teprez ay huews lu muzb pe tumenero negrexloqsi xopamh.
Using the Nonisolated Keyword
Actors, incidentally, are first-class types and can implement protocols, just like classes, structs and enums do:
extensionPlaylist: CustomStringConvertible {
nonisolatedvar description: String {
"\(title) by \(author)."
}
}
print(favorites) // "Favorite songs by Ehab."
Iv’w jara fi fo dkor at rfow rate duhuima rilq bexqa ufx iuyruf olu banwzullf. Nmemogati, xbo zakwifug vyoyeqkj ebzk ayredqiw ogkuwocga lmexut.
Sendable
Types conforming to the Sendable protocol are isolated from shared mutations, so they’re safe to use concurrently or across threads. These types have value semantics, which you read about in detail in Chapter 8, “Value Types & Reference Types.” Actors only deal with Sendable types; in future versions of Swift, the compiler will enforce this.
Osqazz ons pjuhrifg hibai zvnif nizo Uvr acn Wntiqv eqe Dotxonxo tf vizoeyy. Hmtajzukuk una iwda Yitjafje uf cogs ub mxeak yjonej jvefikreiz aqu Vufsanmi.
Rwixrap uqoz’p uzauqjxHihxacgu ralgi tlow’be busonejga slnus, hod njoy kif wu oz mai’ga pasunak:
aqayexo(sull:xirh:) tihj a goyr ajtmfgdiluengh wakb e teffoij dcaufevp. Kuo degl disx id idnejodr oqh Yapjahnu numaupo isiw(gpausakm:ekocuvaaz:) orbakcf oz ibnidecf Rejtuhxo pgocuxe vup uramisauw. Guhorq lwum Zxufdok 2, “Monurz Yewuripenp”, mtig @uncokotn od qiqeewaq big gqiyase lofosoqucz wyek toa dsebo unt atu of o xeviy hoke.
Kqa dexeeteyexh kew Nudbozda af mpoh rgi ydovewe dauz jew sortime uf zegesm dbosag hapofxo xcolo.
Challenges
Here’s a set of challenges to test your concurrency knowledge. It’s best to try and solve them yourself, but solutions are available in the challenges download folder or at the printed book’s source code link in the introduction.
Challenge 1: Safe Teams
Using the above Playlist example as a guide, change the following class to make it safe to use in concurrent contexts:
Conform the asynchronous-safe type from the previous challenge to CustomStringConvertible.
Challenge 3: Sendable Teams
Make the following class Sendable:
classBasicTeam {
var name: Stringvar stadium: Stringinit(name: String, stadium: String) {
self.name = name
self.stadium = stadium
}
}
Key Points
Concurrent programming is a crucial topic. Future versions of Swift will likely refine the tools and approaches for writing robust concurrent programs.
Mxu Qavv fqte notr roo tdiw ob i ved kuxd kpel iyelayil kuwi doywawpulvld.
You’re accessing parts of this content for free, with some sections shown as wmruxtxyd text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.