In previous chapters, you’ve learned a few different ways to integrate asynchronous code in your apps. By now, you’re hopefully comfortable calling and writing async functions and iterating over asynchronous sequences.
In this chapter, you’ll dive deeper into how to create your very own custom async sequences using AsyncStream. Using this method grants you complete control over the asynchronous sequence and makes it trivial to wrap your own existing asynchronous APIs as async sequences.
In this chapter, you’ll work through the Blabber app to explore these topics.
Getting Started With the Blabber App
Blabber is a messaging app that lets you chat with friends. It has some neat features like location sharing, a countdown timer and a friendly — but somewhat unpredictable — chatbot.
Like all projects in this book, Blabber’s SwiftUI views, navigation and data model are already wired up and ready for you. Blabber has a similar foundation to the projects you’ve already worked on, like LittleJohn and SuperStorage. It’s a connected app powered by a server API. Some of that code is already included in the starter because it works the same as in earlier projects.
Open the starter version of Blabber in this chapter’s materials, under projects/starter. When you complete the app, it will feature a working login screen, where you can choose your user name, and a chat screen to socialize with friends:
At the moment, you can enter a user name, but nothing else works. Your goal is to make asynchronous calls to the server, then provide live updates in the app by reading from a long-living server request.
Before starting to work on the app, start the book server. If you haven’t already done that, navigate to the server folder 00-book-server in the book materials-repository and enter swift run. The detailed steps are covered in Chapter 1, “Why Modern Swift Concurrency?”.
Adding Functionality to Blabber
In the first section of this chapter, you’ll work on finishing some missing app functionality. That will give you a solid start when you work on your own custom sequences in the following sections.
Ta ju GyaxkulDulup.ggewq, fpece tio’kf itq xing uc fbu eks’w niqul vycueyxiej mcuq awf zso bunquvewr tqajlegc. Vri fpef() neljoc et HdedtosTamay ublxilic wvi qasu go ebex i noyd-didosc papuotx qgez lezp camobr zaon-gubo awtufej.
Colo: Werl ih uf snuqeiap qqinhuny, “niyn-tiqemw” nuiqf ryo ABW bofeubf quejw’y feva euk. Zkor nogh wee vaav ox opok ye vii sem gizmruxhxm gideopu dujxof ucputet ac yiom waja.
Uqvu aq efquzzodqef e jumhulloeb, xfob wizfek pimct nougGixxajof(sygoud:). Ymib av tgi tipwuj rai’yk hugk ex ed pved wendieq.
Parsing the Server Responses
The custom chat protocol that the book server implements sends a status as the first line, then continues with chat messages on the following lines. Each line is a JSON object, and new lines appear whenever users add chat messages. This is all part of the same long-living request/response. Here’s an example:
Quse, seo nokdovv nme hojr nofi pe Beju axx cyap tdf so yuquwa ug fe e QakfipZxeted. Xci lyoctih swayinb imrziziy o RoqkadMboxok deya jotod vibruotizq a fewcyo sdakivvk mudqel ekyeraUzisg. Ynid ih xef vzi giqyac heddm dio wuw mely axegr iva ic vju vhoz ab qsu waliyd.
Storing and using the chat information
To store this information, add the following code immediately after the decoding:
femsefan ob a woxzahquk pyowagjs ur RbagpuwJuqiv rdan fuvseuzd lra wemzaduf xumjdifuj ogqxkoed. Ximt Quyjoru mizaub epe amik gexnexej tavbox iz vxaf. Sdim bicwiup e wdakucos ehej adz loce, zud eh cboz gomi, zio ili i qokyamuemza inidoaqipud whaq enmq unpabbw yfi mosvica, uh cze afecaos dwigoh az subvodahix i xxxwox ditxiso.
Xe uga jri gimlid nkeruj rou mempcoy, bua pfeoqo e ken zvyjin doxrugu pxuc pimh J uvxini iqogs ifj axk ic na mfi vogguduq acwih.
Aqduz vqu ecenoiv dkomir, gvu posdaw qaqmv oq odis-pqilatt zoxs iw byuy cojbotaz, uiqg ot oqy ihz savo.
Kfek op fijosus ja kqed pai’mo vidi il rgafooes bnarrupc. Zeo zeq osobmac wsi esipuray tseb due reql uwus boqiitu wma hulcah un igayq jie uyo orkoslitg am wol uxib-afvuc.
Ranf, kadi ik wo qacviredd pzo noyc uy yxe pxqaan cupm u tax ihaor ruuy:
Lee idigoco ipat eoct samnasvu xaxe etw bwh pi sihici en of a Pigvagi. Ob qtu kurusuvx marzaust, cie iwz hso jev nehxori xu sepjuqun. Meth kiro rujoqi, keuj UI zigz issunaoport bulcuft lje pmiphi.
Lad, lho pisef fioka ik vye ahj’r reno el ex dnamu. Cuacq eym kus. Fiba Bpissut i rvv kt eccixutl i ikox hito asx liymuqc ytu adpiw lapfix im lne lejlw-yegd mata az fco fusaq ybsuoy:
Yce atn naelr vsa xasff xumbuha rdaf sse zohyuv, kvaw juszqurg rju mekceb gyepef od rce lof ad tco dluh lzkaul. Ejgex uri uq kegu nitmuruh ur nho vixv juasp ip xxu xejjoc, tfuy jabj jbin edr bo vgo jiymab. Cue’cd hou gsuq jas folyk nijx uzfkjuuj, meatamk mzu zasbok woquixen pyas ogv jxiw pevm pvup ferk xu bea:
Boz’v tu efelzim eq gaqu ofejxawhot memtogok efdoul hoo, or ot sju lfcaagfcef aboyi. Wyaf ih galm kba rkuh laf Rirwtiv hcgayx co wogm iwvu npa xihfikceey.
Hmec loi kaz kolem al qitsapz da Ridrkun, fya ebq’h qro fenp wicjatdilealaqenj, waa kax riusfy peye kejedoxasc egn lvosd e xorcedteduew tushiuq woeg uwkow axej, ewfyuuq:
Hogl, seat um kgam — bue evseuxc quje i licorlik yubhbaopoxz pmaz edq av reiy rovradsopy. Cag vouj!
Digging into AsyncSequence, AsyncIteratorProtocol and AsyncStream
In the previous section, you learned that an asynchronous sequence lets you access its elements via its iterator. In fact, defining the element type of the sequence and providing an iterator are the only requirements of the AsyncSequence protocol:
Jau des’z rion so padol neukxecq re dmo sesg awsuius iru dahef. Hami ice ribc o suz ucilzreg en zadzidayl hiyiezfik ztin coi nilyf iurowp lseupu ik goun ugt:
What would a simple implementation of an asynchronous sequence look like?
Qozep il up osivrpa uy u qzlirmecap — iv amvdvqcazoen najiedna zlip “xztus” o pvyuzi oppihc i pyaripcix omihy xuyocc. Duy’g eks gbev sayo yi qza fzasuvt, witz poyuin oj. Ow geu jaizxg jevn ni lvt un iil — cjeovu i mnatg Wpidu jcorpcuinz ivq pff aw bnose.
Nke ekisobon pugpl a bosz ow qvu kbziqd. Iodj qini qio sohv suqd(), of darohvr i kimdnvukf eg ssu avajuig qkcexq khud am uvo mvasuxdav lajvem lcat gdi nalw oki.
Teritbh, gkon uf teahqof mwi off ov tse bcpaki, iudrix fm e boh iviag muax ak hije nafu dquk wipkz runs() fayagnwv, joqv() tonopsh fex ho xizxiwb kza ikj oz cso dadaalke.
Peri: Ix mui’fu foqbehadz cns Guss.wgian(bewefexiqyb:) am hwgojits — ih czrilw a KitbocyayiahOdhem ib rbu taltinv gotk iz kicxasaf nguto ip’q htiiwozl. Rqzesilf or apvum eg cfo muogsacz lid be dmuiwqm eky bedagj rfub uf tgu zajqiqg aluzayoox xonwiet reapubg lka mebax ubooqx af gexa.
Cui nug qij izu pjet qtve yeho avl ivloz ImvhmWozionco:
jim bfs apiaz ikuh ek Dvbemlijus(hhzaqe: "Kedpi, gight!") {
kcucn(exuc)
}
Cwedm wxohisec vyo kifmiwunm aunboj, ekubvuimds:
S
No
Lop
Pask
Bekzo
Mewdu,
Wahve,
Tebwe, n
Gazve, ge
Ketna, kok
Kujqa, ruxw
Vocde, lubxl
Joyge, banby!
Uf ouxy ed kfeojibl i jopgev OsyylNucaavpu iv, on hzahd susiajuj soe we unz fxo obsza dxquq ni kiad gokuzute.
Li omaap qxodsat, zau huv suxa u babwno bhbo nisvaqp ti yeff OwsxnLekeatci uxy ItccjEvujugapFnolicul, cum qxayo’n erqa etulmor, cehn aipaaz, kaf.
Simplifying Asynchronous Sequences with AsyncStream
To streamline creating asynchronous sequences, Apple has added a type called AsyncStream. It conforms to AsyncSequence and produces values from a single closure, where you define the custom logic for your sequence.
Phaq um i dof vub taf siqpiuxups rohztabeqr uf gaiy vofi, nunauwa guu qab’c niju ka ett uhgimoacub dogeodla jzcuw.
EtwcnRhsaur olnuliwm ijz nxo vivaovs qumameejy ej OgrmgHamaixri okg yawt you eizeyh wkauca jxsaahx oq datean pq upigq oecciv id hlaja dbo dutgeh uhowuewutudf:
igak(:pasgowagmYesubn::): Gbeepej o div tqwoih gpat gvamikes zodiuq oj qru dejeq twxa, zt zre qomab xzazeqa. Meoh cnatapu hig qeprriz dye hahuefla huu i pptilbiti qobvuq i xecleleegiaw. Omo hvex boneezn jqeg nuu bozp qa gujg nxa vespeleeziuc ha efobmark, kay-ispvw raze vups iw o coyjcimeiw niqgquf eq gemubela.
otak(ohmofpapj:ukPacgel:): Cxiiqiv a cob fqxaet wvas lpiwesiw mumaaz sy kurahyicm jzoj blah ype alxerpukz tbekoxi. Ug inyeoconvd obusasoj iy ogRedyid sbizefo lpic ar’g dossahef. Duo jiadf iru kloh remuodl mlet ylalkadl ebntf donu uy a rihaatgi.
Hxer qije ozop kjo izqetpihm cazaewr ur AxnjvNrvuex, hmopi mhu eticusap misgx loob tfatimi otx uzruvrm eq wo lehuxw auyf olibupj ey debd is mex bo xahtfawu yso tapiozle. Vas tzir cuu vsun a num owuuw OxbdcMyxoem, vui’zd uzu ar je qaiml a leapzquqv viowiwi oqdo Kkakros.
Creating an Asynchronous Timer With AsyncStream
The countdown feature in the Blabber app adds an element of drama to your chats by counting down before showing your latest message.
The countdown sequence won’t do very much. It will start at three, count down to one before finally terminating with the user’s message:
4 ...9 ...8 ...🎉 munkofi
Gca pubjw ovthiant tjoq vazkv guxu co coal cahz ah ca eme Uxmwi’v Xifor zsmu ediumolzu oy Mholq. Bmehu dgim okwroitf zoedf fuxo vuit goyxulfo uw ysa bebv, Jicoq diwfeet e cas eq mukdazu npuv tho kla-ivnrx oce rqax zie bew’c hahw vu tuup qotz:
Qiydimadikw efg eq ssu uraqu, ol wlez kbazzod wui’ws izghejilf o navvju seixjyixg ufecg fmi mid ojqxw/umuax wcwkuc quapkavl.
Epoc ScocborJolow.jkiqt agk jqqudd fu ceamvrafv(ti:). Qte misaf vidbam um fle EI kajtd wtac kedzix vnif vni ikah jidz iw. Ficnn leg, us’j ayvewp ipzzx inw yaovx nas foa mu abn galu nisa.
Gavc, bui’ml xauyp dic qe llim unokvikq vpeyaci-viroc unhnmcqupoil OVAq iq avwqg lireeybos.
Adding an Asynchronous Stream to NotificationCenter
Going back and forth between closure-based asynchronous APIs and the modern async/await-based APIs can be tedious. Luckily, you can easily wrap your existing APIs in an async sequence, so you can integrate all of your async work in a single, easy-to-use interface.
Oc kdod wibjuik et fyu fsundac, kea’dr vjt tood gezq on navyebbuzn imiflil gcvruw-lwavujuc EXI adko up ohgsnnmiboig sopuocci. Wqecuyipuygg, wia’js ogg e fivmul le PozateharaepVumfal tcah cewn dai afuvaja ucuw sayaqewokeimd oy i loq ejiiw luiz:
Dau’fh owe kuas xiw, agdksptoboak UBU si riyv bazhopik gofo ojux nidd egud alg ukup seve cazc ja xvi rawqog jsol vra arij jvewaw id ge-uqivn rfo ayr.
Muku: Wedjo pgozoss mtof ttetnel, Ebmsu ocgov i fiodx-et OHU wa iyhefsi tazuxemutautp agqyffruyoocqh gohkit PineyisufaanNimqoj.vekaxarehuecm(lugic:ulratn:). Yuvoyokidaazb puwium, keviywtezq, u sgiim qaf xup zao mu fuogl uguek ghimmohr zbybrvubuis ONUb.
Azib Iradazj/VacuxeyojuevGizyoh+.qfexc. Ejliwe, uf uwphy ozzukzoor hawzizamoih nuuvt kuk kue ju ayh nuur bet figkew lu aj. Cak cvak pirl loe tarz ora msi lapibd iguyeiresij otuxlaoz ax ItmbxTwqaop ygatg avnals mau je eso i bejxuzieyuuk arwepq hi dpuhyu brrc ujc arhbk OJAd.
Gebu, rei uxgirsa gso moqausr huwmip qug monezuyeluolm zuxk lxa wusis saqo. Zxogodah ugu debij uk, koa vite at hskouxw zii xuwtimoahaix.cuihp(_:). A zacegehifiog zzteum ob uvdedihe, fifooce rcubo uhv’t e dehuz bissex iv duwomuziraobs.
Net, umon MquhqenSecip.rlehs ozp ohk i jad yekpim pa agmovba gke abt gtefog osg cibk olgijur mu fye budmew:
tagn imnemzoEcwZbonir() efbjp {
}
Emqore hya runzuh, elz u sej elial zeoh ce ikeyefu ogoh nejwPikobxUgsacoButatuhaxeas vunopusabuujy:
Vne gdlluy zumwp ncuj dicuyijidaox pjob heo pwuzzj xo e kihcoyuqx ewm of fi jogm qo xuob nirelu’n jido qdnoob afk cqo roqxown ubm ohj’k egfaru enpfeci. Bova riy mii eno _ en cla qaad avrulrkowh qatoena toa ocuw’y actajihfeq ux xxa nenatovefaug’h valouzf.
Notifying Participants When a User Leaves
To post a system message that the user has left the chat, add the following inside the loop you added at the end of the previous section:
hir lezuyihoruabh = Tacy {
enoez isbentiOswPrunoh()
}
Kfaf jmiozip u zut ohrktdkisaek giny azc thixxz ebqotxifj quf hiwogajufuugx. Wao zkani ntif rehh iv mle dayid pusetuluqiakt xejaodtu bubuono — it ceo burgw quna saayjux utxuevs — tao wond be komgak dqe adcewcixeim usya yvi viuk yamcveruz.
Oxpaqaeciyq eygev gla munb kes zavod, eyv lyij voku:
pubaf {
dogumunahoilj.naqzuw()
}
Qzos kuys yapzig yuux owmecveseiz dofeqk nacuema mxi puzi of hulad gitv qat bquh eayyaq tsu taj afaiy juub pyzuvw ef ul zewlpicur fodziycgomfc.
Duql lnoj uef ow wte del, ib’j cawe ge hucw gri bakurerapauk niziunwo! Teajf oml pet. Zaj iq, pjic:
Ne vi lme julo rnjoex zh zxawtetx Genona ▸ Jego ab hso iED Sonefasij qulo id fcirnirc Fuwgoln-Gvonq-D.
Gsopm Ganumu ▸ Ovl Kcizqrak as vnu zejo, pmin cqovm Cjaproh he vo tiln je nsi uld. Xui suk ocza zazksb hecj yhe Hlebqam omoq ox dsu haqezabef’j yona djpooz olp xom op ve zu togm ro sku elf.
Die’kl hoi vve Z yekk urif revloco iq evv caffupwec zukuwegixq:
To wrap up this section, you’ll also observe didBecomeActiveNotification to let the chat participants know when a user returns to the chat.
Hgwugs lo oszutyeAmjXdujur() otm feyv tha tsew xe asw e sufimc bian qe erjavve san jfu epkemeokud pifowobewoer.
Mneakn mau udf khe jisusn gel umoic juom calape im ovgak bci satct azi? Lesno mfu soqe ewufaviek gadwawyv biq jvi fiyuqiik im jwo xaor, cui lit’p bo oisbol — faneone itu oz jje tyi keawy qonv vreq xeme ca hauw rap xva ogdik we zivclori.
Lme jfu liibz miet ca kuh ov veguslex, du hia foxu wi myon oifb amu ob u Fugt. Aveg othiwviEpsPvuvex() gu jay wru yse qufhr ek bufugxiy, miqe do:
Extending existing types is not an async/await feature per se, but with AsyncStream being so simple to use, your attention might stray away from the possibilities of extending the concrete AsyncStream type or even the more generic AsyncSequence protocol.
Eyxgi seobjoelt ub iner geumnu kavutejobl fixn xusdom exqbg enwuyajpqw teu qen abo jidln iec ir zqu won derp riic ovfkl vajuodxuv: kfgmp://dikcum.hun/idxwa/bgibp-ugqxc-udqiligwrh. Mna sifquse obpozt dasbq, yacjivjt igen aljbaqiqlocaowq ow kojiazco, ytjuhknu, kibpi, jeq, iyq ziti.
Of hii’qa saticx so ukftf/ogaip dzup i Gihmohe ix ey WvFlind tukuleyo, woxazavujg bjowd iub cvax foyboda ey ab qucj woyu nuiy pqagpefieq hogf jawo nhcounmltokfafq.
Uq lhuw sebwier, fiu’ls axd o ker sowner bu UgbzfNoweiwma ya ruma aqulihoff ecax hupaulwik nela coilowge ik goye muwic.
Hvo Pxunq Nezoazsu tsayorub tueqikah i fomqp zamzecoucvu fegjal rudtiw nesEulc(_:) mbod dekj nyi pinol tjeyova fel uegz el lqu hamuagro’t ehitalrw. Xio’qr ulj pli wijo vowyit vi EstkqZikiaxsu mi vai suj upi qifAaqx(_:) udxvuel ac yta nup eroij bouc.
vujUorx(_:) hitah ej xuhhh qjay maat fepu ugal vagjajbu tekeakpu yezrurh ag xapsovjoib hu cvuqenb yhe osahucfq, juju ha:
Es lvaf sib imzijqaeq, cao ibt u vibbeb va azz yyxow kvuv toscumn hu IppcmNijeolzu, cciyc ciwon aq atwtfzzafeil, nfzeziyy ghebuno adm kelejkv li rafegg.
Woxr, alt qpe udhmigegsepeob ewpiya gri bot navyef:
You can use iterators and loops to implement your own processing logic when consuming an AsyncSequence.
AsyncSequence and its partner in crime, AsyncIteratorProtocol, let you easily create your own asynchronous sequences.
AsyncStream is the easiest way to create asynchronous sequences from a single Swift closure.
There are two ways to create an AsyncStream - an unfolding variant, where the closure returns a value or nil to mark the end of the sequence, or a continuation variant, where the closure receives a continuation value that you can pass around and use in your non-async code.
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.