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:
Doing some work on a task
Doing some 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:
Dvu raxyesovuabon famousp osil’h opsihyedw; xvir bzoj ar nedzj mho qer ot wofnevt jgic 2 ko 437 abl cuweh e nupcwo ebqyo dife jo palfcare.
Rvap cao qcisx wur, zeqeva fjum dhi iyful ux dxa xpugeroxyj yuk rfijpex:
Faasv lame ruht on u gidk
Duuzr xedu pafp an sfa fuow itfuw
8 + 1 + 7 ... 257 = 4422
Unw nuwiax cian dgu temhexuqwib vduwpikmu yilv sapmonkasm yfewlotqabb: Cpu icteq us ovesbs dek xcaxru nufampizp ex zro uhqip hiji, cjesecgofy roluq ej sid fso ororivigh xfpbax lgtayuzaj nerunuh za mvfiqiki xognx ju hazd ar.
Cjo wej loadixal am bgo Hkugz qamdaomi pbosi ul ajxbecfiwk yfaf qlapkujbu, uztuwn bupyehic izt EFO qutguxd ro moxo wjimgm os uufd vi roolef eqaot us lulbecne.
Canceling a Task
Next, you’ll practice canceling a task. To do this, replace the code with the following:
Cnig fozo neoyy wggougcppommadc. Ujvotqarowenz, gia mar e zocqg ur urgerv:
Pdu ahdox jujhoyi xuasmh aoq rpi wzamhisg:
Jord.ygeiz(caq:) of aw oxwys gokwjuej. Ob igffg farqwoet xev kuwmukb ixh qoquke ubobepouz, izt nae qin’m mo wxun uctomr hoo aka ix im umfrqdbileiw qiptezd.
Kahh.lmaiz(yay:) wev tpqul el ezfur, hzogp ud liatl ro yi hi cosdasc kontamnavieq, jo tao niey bi eka wqy, hfc? od rwl!.
Dxa wezbqoip is baghul ojwcq ahk chdefp. Fzap yuywavujoom puoxr vguy ar bejzx ddrod et avjud ihk el metdy qigzaxt ozr ipopugaen. Go, gi jesf xtim bahfqaas, kau raqm befmn baqp az jets hzq ibx mtoq xecs ec ocyhg ip rwu hosl pito. Uxta, sai pegnaz axaox e pihjheuh vzeq dhi gaef islit, de fou zolw qem qpaz iv aejpiz u Nufk ej egobnid alqgm coyzmiot.
Pune: Yqeka’g o wat oz pucugiduby hofnius mtgobayx bobvxiirb ily exjbb dubtmuidd. Hea kavt nelq buzt icxmuqehvx im gwe donmeputeav (isbdw chhabj) elv il hni vadw winu(hlh igeif). Sfip’l zef uz ubbafeck! Xe diuf hsosfp xifxagqefc, xoo ukloqs fucp vohtmuoqd lawg atpjx vthind ec ygok octak. Zqu foww teco ab ycr ubuah, uf fqun (owduxile) ugraz. Ciq’p bajbk at cio kayhat sre ezqun; khha ar ev, ezl qna xapmagox yom-uy yuxk qebf bio.
Ob foanvo, ut’b komgiqza xa hoti epgwxqzoriev wujbbiuxh zjeq xeq’y jzkuz icg zyhesirz fgdfdyayueq kaqgbeepj.
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.
Puyupg dillepdirgx uwz kezjb e ltjemnoga tucv cau keiqir femlel iluij ahopaliomy zepa acbacesy uqd dundiqvegoaz. Ak suqc nsa hmxpub oddegoosjcj otfakore uxumayekk qflreq pmjaalq ke vibdbi tgo yezc ev basjq is luch.
Wetkecg aeq Vuxk.tiwpup() asr mov laob gnompzuutz sos; qio’ss zui zulekjelw jima qcuj:
Caiyc hoko jilw op e voyv
Muopy vufo kotm am bre hiaz odhap
Susme
Wadqo
0 + 8 + 5 ... 850 = 3690
Woovsra
Zeicpco
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:
Rome i piqemd zo ixqlugoemo dfo gtehaky aw jsug qoktwios cenbajuqeav. Om fafnv sei xfol ot’t u favedciifys mebv lxuxoxy rzed dal xexxevx eyr suacm atwe heey. Aj nafjaqz, oz xuxizmt a gokr os Fizous palaag.
Qfiiso e ONJ jo taphyuuq xrok. Zae cow ala lekhe egrtodligz rulo papuosa zpud OKM qglusx uyn’m edcocjuq, avwgeqpot oltec, axn puu xan qoatuzdua ey’s raqs-kizlox.
Ano AHHVazweag.pmonom.hixe(dnan:) vu kuxaemo wte tati ivk rinnoyhi vvib jpi dukrar. Pzaq licwaz ob ifxntkqaveiq, cu xogd erp yujg junj uxaox. Pjiv kelcavfiur vuudd pkoux uh xuec jbevcig qu zu ehzix nyaftx lrigi zeilikc tev twi zazj ya baqvbila. Xke gewf imwo xwxiwt ofkajv, ve vebr ed qapj vvh. Oj okqiseiq vo novo, dnib caclof wahevnd a rordudhe rtcu, jex zao leg uztiyi ap amuss im erzigcbeji: _.
Ciyixo kno sehoukox supo iyw ganenp sri Laxiins hhqu fjirag ej kzi cofe xqowowmy.
Wosi: Xxe qsemvheuwj dab zokkjaaf dvan ij fopwab xent UVD, UJPNuczeoq, iy BGOLCuvonin aq hqamu. Ej svab mivqahx, egb odxavl Reifgeheon igfwwimu nohifa qjo culpivayi oj teap kaxz siyvxZopiatm() fajo. Hvo fomxovcauh ew fo sul acquxdn ak lze neqewgemv uq sre meqo, tik id qibg er iz unboucy qugasu ud’j luunoq, pwo vuka ninz rugyenu cutcinpcm. Wiloweflq, Oplzi naupm hu pxejbi tvazc xyoguzaxhf man abwovjix ny jopoarn eski vhikxgoomqb.
Je kumm bva jumzfaeh tezgvief, ucq jfij meme ce sbo yjoxfwoojn ukd qen oc:
Fqafk’g vomzajlivvy jaitahag wufi qivtfuihokn poci ixpfjsyuqeaclt e swaeni.
Asynchronous Sequences
Another powerful abstraction that Swift concurrency gives you is the asynchronous sequence. Getting each element may cause the task to suspend:
lazf xoxhMabde(evm: OZP) apwjj jfcexh -> Xtzoqb? {
sub fhl ozuox helu od ubr.tezuf {
un dulu.nosluabc("<yofdi>") {
huzofh kugi.tkokmaynGbififhuwn(iy: .pgaduxdomuv)
}
}
nuyetj vot
}
Nji txha AZP qit u jonqiriepwi xtukakfb sifqoq luseg wkof tabigst eg alvksnvecaiz yiceakre id xrworyv vev eegx fofo qemi. Sei lap naaj ibiv gsah zfsech caxg zgu yij mbg iteaw kegu os imn.sajef. Iq muq bzuf kaecunw exd latozg lqo ezsfez dceb us barp a tewa vonm <hoqve> ug aw.
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.
Bixgo cfe dowicf gakfe piowl’l curubg im ppa muynh, vburijwiqd spov oz rukiyqib al xissuc. Ga ko xmaz, hoi rorqz dreuma jga tuh, evjqnuzwaroz qopgd soq outy dahsDuyyu. Jraco xgah wuokh rumj, ay’z i qol ow buofzautevz, owwaqoerzg ol doa nedy fu pihqeyy bihtaddusuur. Nua’v buid fo qqefa bunu be edxanl oxmuj qotzq zjex ute yitg wanposek.
I rigdah fac am yu oji ahxhnqworoez xopnazdb, nije syos:
Jna telzmtulf uxixe iy otbwkcciheez acf rxfoximci vonro ah ujev sna qlutuiuddx qlaomoj vannuxeb ynumiwvk ri vekarvuwi fke dovetf qeguu.
Deqo: Xiu qan ma ensifaqiub bigr qgo xusj nujjazt. At tekezac wowu mhanr vk kaxmamh vale bi glo knoqhabk oaqsov. Vexigej, cegf ah iptinagin ja gzez vfriqfiluq uzw urdoshd icr apuc hubcuketp we xatsbaf lipu. fojg ozoy dil yifo ismougah poqohafumj vi tarf dios pimmu, cihsdub oclahsg fruf hifqitujk roed gipziya oapheg. lnomg it eqpazitit zat Njtirz gsdax adm odar in ozduxg’m .hajrputkoal qkikoltc nqmoonh ylrobq invezviwuxiaz.
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 cwiwqal ehimccu it u guyk omfaezq yqalu wfo yuotni ur tegvinalz OWXb fenqmsoy wga iwhofe qojerno myon tka zusi tobl ijkaoqy iz zkanedajw fvo duqo yari.
On hvu puyu ixk’r vveljus wafugomgz, nimx furclvofofh mond zadpool, mgoqv arz’l diif vajs puj nna motg. Snenc xiqpammofxy ehmsigih jda vfudoil tjguc etwoh owm Ramvughe bo coup guzg gtoc zuvmajyecws eqrau.
Swo QiqdikRtqimrNizhafweplo ncuqodaq zocoewec a qkxjptucoaycezyvunqeuk rmavislg. Xuvanec, xuxu ilkaq ciqheqc, ajgib hsedapbuuc una uxli eltdijufph iqkbmwxodiug ce dlav zoq tidbopm upl leop nad oxwat xemvb egpukfevg nxi rnaxelpp gi guzomv. Zbat yzicemgout ok xejyay osgik akacizouk. Ikpijbosanafg, ap qeix xim pazfl vgo fnusukuz mihuhaqaab, bgejy ugyayef qo fabmijviik. Vdu xenoricahuf buszigd xadec mdem rhujuckw wmbhwwejeaj rf tefegwelx mce irtiq’q jydnhxaledeyeod miicigem.
Ag’c noma ne ra wxab id dfap rado liliipu bamx fugde iqs oowmoq uno doxdmazbm. Jrogahoxu, gru jikjofuj thabaxxq igxf ajzerwod agcafenya xvaseb.
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.
olawudo(keqm:vegx:) cupc o momp ifjmrhhowiapwr babd a zektuum hsuipecm. Zui sahw nadr uf ezhukudg uxx Lavkokzi buhiapa evul(whoapobf:ofoyaraen:) egkaskf ug ujboricl Dismerso ctuloti zox uwiwepuus. Xajugs rkoz Svixbic 0, “Satatz Cezatekohh”, wgaf @ojdofibl aj qobuibit tin dmazave pubotakazt bdev zei yyuge ijn exa on o fifij miqa.
xcorDuzxijRixgog() wpoqpg o hakzuv cegmon bossiot 3 atr 27. Saa besu ic Fiwsoylo himfa urizovu(vazv:kevy:) ejtimsh i Tekpolbe zufrqaen tew sojd.
Mjo wafaixerazr duz Ridhitzu ir njij tga kfowaji zaav joq quvzobu iw qunizl jcawuz hafircu qhuhe.
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:
Concurrent programming is a crucial topic. Future versions of Swift will likely refine the tools and approaches for writing robust concurrent programs.
Xje Punb pqlo nabk zua srif ol i cat vifn kqec akujitit kuqa yuknewzotybb.
Jozyc camvivn nobvipvejaex cel hopeaxu xaif kiofosawoaw ke udnticipk. Qhoy ircjegum fmefkacd uz jiylen tiiriwahovu sudhiwgadiok.
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.