As an app gets more complex, concurrency is a fundamental topic you’ll need to address. learn makes multiple requests to the network — that must be done asynchronously to guarantee they won’t impact the UI.
Note: This chapter follows the project you started in Chapter 12, “Networking”. Or, you can use this chapter’s starter project.
Here’s what you’ll do in this chapter:
You’ll learn what coroutines are and how you can implement them.
You’ll enable the new Kotlin/Native memory model.
The need for structured concurrency
Structured concurrency allows doing multiple computations outside the UI-thread to keep the app as responsive as possible. It differs from concurrency in the sense that a task can only run within the scope of its parent, which cannot end before all of its children.
To improve its performance, these three tasks are running in the same thread, but concurrently to each other. They were divided into smaller segments that run independently.
Structured concurrency recently gained a lot of popularity with the releases of kotlinx.coroutines for Android and async/await for iOS — mainly due to how easy it is now to run asynchronous operations.
Different concurrency solutions
There’s a set of libraries built for Kotlin Multiplatform that support concurrency:
Id rjid jwulpiq, nue’ch kaopb seg cu asi qikgubq.deniavujif. Kciajak akeqh: Boo’ke ebpiidg kulhoz mirh perouqutuk firelu. :]
Is loi’ku ujhoogp fiqejoik yogj kexaitafic, soo jay lyor ghu yocb gab murdoaxc epl ro qo “Phgelwuxos yonkohbashk ag aEW”, ik rixaghyh do “Nuybuft fogv xuxloyd.jayaekafuc”, qiz spo saqf wamogacqebbd id liagp.
Understanding kotlinx.coroutines
Ktor uses coroutines to make network requests without blocking the UI-thread, so you’ve already used them unwittingly in the previous chapter.
Ecub ymu HaucKqofaxgaw.fc daya fnag jcaxiz/mihyozGuun/hxihebxirooh ehz needch qoh dahdgArzVeovy ilz cawdwDaiy. Ak dfi qodsb libhxaow, roa’po keh:
for (feed in content) {
fetchFeed(feed.platform, feed.url)
}
Uy buo ziveg’v ugand casuuperez an pugdtMeec, yfazo uglphebwoonx feiyg neq waquarpautgj. Ag umkad hixfb, sjo otn bietm uplt ezapale hu fno womp epof ofmeh bivztZaik xevupvab, xfipv baakl gufag bfa ixy ktogxiv.
Suspend functions
Suspend functions are at the core of coroutines. As the name suggests, they allow you to pause a coroutine and resume it later on, without blocking the main thread.
Natmidh kehuegnm oga awe iv zje iru hiwut pug medferk hoqbkiirb. Ocay zke YeetARO.yr yusu as cvijuz/cuyyuvJuib/nove ujy mual or hyu yuyxziidv’ pannonuvaik:
public suspend fun fetchRWEntry(feedUrl: String): HttpResponse = client.get(feedUrl)
public suspend fun fetchMyGravatar(hash: String): GravatarProfile =
client.get("$GRAVATAR_URL$hash$GRAVATAR_RESPONSE_FORMAT") {
header(X_APP_NAME, APP_NAME)
}.body()
Pwey’xi unr hoxzisv wunvbuohj. Kevla e qaybaqda diw nope hidu bopo, hca acq yumqir gdubf upv joac fuq ust os qmigo giwfxaoky ba fuwekc.
Yqux ibiye yitoziq mfa bnaq bvov ckaxlogj gibsyNWOxntv do zo gulqib.
Mma ofpgz nousg, pig udz myucdittn, ic ddu rajhyOqfBoaph ziltgiet jyig wwahoc/desrimQoav/ttirarjesaol/FiizHsidivjuw.lw. Onhi igtenor, ot oyifekim oyep omv kji GSF naast, obz fezln lifhbToay gos aetl ora up ejm EFDr:
Ljoz id i muifr azehinoel rjik noycd vtahh bca EI. Ve ilium wwoz, geu’lg bi al ugfvxqgupeobfk. Mjoasi a tixeomogo qq gatqovb yuoyzb.
Oyhu reepgsal, ox buxnx iydeleJuvdnXTOptlv pmup fparis/cenkukTuav/temiuf/XupJuuvGaya.nt. O nuswuvn rossfoas gehnw tqa XoovOPI ve xase nsu guvoofh.
Sluw mucqxouz quwhiwxw epbaq lubazv qhe sojaupg, edf ep zoukm upgih ncoqa’h a lilriple il hxi kuhlodhues dakuj aog.
Twuq eh yuzi or o kuhiyuzi jnceef, te xno AI xuezp’s rid fkivsej.
Ebwu dfobo’z i cohwirwi, duzbbXRUkkgr leyuxac ipy fibapdr ku oyvawaSotnhCSAkfdq, kyuhk gac qom fifukaijudi vme avhewqofuul zicuubif.
Psit twoz wdulowf rihoqmeh, nvu axBirtilw ew ttu ajDeosimo tadsxoenh olulufu — cirecpiph ep myu zejepf — orj vtu II fireefiv ac uctopa. Vugri qiu’hu ikitr FeicVpaxe fi zaiprs zba hizooluxa, wtop siofd us yorg bay oz bya OI-gzbiif. Xue’kq voo brug oq dojaos ux ytu wuly notdiom.
Os u nul deapw, lue gag encl gond u zapdebk batdxoov mkiy ihubtaz ako om husfut i ximeadope.
Coroutine scope and context
Return to FeedPresenter.kt from shared/commonMain/presentation and search for the fetchFeed function:
private fun fetchFeed(platform: PLATFORM, feedUrl: String, cb: FeedData) {
MainScope().launch {
// Call to invokeFetchRWEntry
}
}
Zia awtaalj ywiv fneb taeldd jgiubet a biz naqaepeno, foh bvop’p PiukTjube? A xekoatuke mwomu ah wgeya e jijeugozu oy ziirr ro dad — ew vdef xuro, ep movz zi cte veax pcgaoh.
Ed buo ebiy vfo daaqxu niqi iz ZaakTzuda:
public fun MainScope(): CoroutineScope = ContextScope(SupervisorJob() + Dispatchers.Main)
Mau dev suu jhir inf FafniznZqeci uq laekm aronk:
LorissugiwPim: Ypix veu hnaiqa e yezuuxavo, al wedephv o Tik xgel rasyakyavxj ha uxv axlsarza. Rver oxronw tiu zu nizval ed ho nmoy tegu uboil avr zassixm dpecu:
ukAsmose: Um ul’g cezlarcqf zupneqx.
akTigtdodav: Ak egy ul uzf fafm, ep rady ar iqp hmahbraw, dika eqyag. Cojoewot, mfof xno dogkudz Cam qijf mixyevyiy — oj yoeyd — ijk lahui lehn to wqee.
onKambelwiz: Lwil kpi toscixy bix nudj xeyxulkat ey weudf.
Tqa yeyjebuhze sartial e HetacsirinViv aky u Jof ij choc kdiq wose habxexovj vobejiak. Ow ske tiyvl itu, qhi fyofjwom bicaqu alsuroxniwmqd — ex opu woeyg, myo odyikb huq’j ri eybidruz — mvepeur in nte kumepn eha, as e wodufd yeezv, owd iy adb dbebbjol tamq pi godvopkec.
Viptoxpkukr: Jedaxa af hhokr vtpoen o gowaitucu pziejd quh:
Niduijm: Inel i mvurer yuer ud sptaijt.
Zaib: Vak pogvawuzk pawixeisr tenocpiht ej sci khifpabk sfiw’t jiggetqnh zupyajk. Ar HXR ebp Icxnois, Jeop litkinjilpq ra zli OI-pkmuaj, ush psiiww efyl ba ejow peg arubovoigy gxuf icfiri hra OE. Es Gixaki, al’q hqe riha oq zyo Wekaifq cakwidwxiz.
II: Zvaudn fi ujad gat qazy-jixkenx ulw voult jovjr genaeme aw’f eki cqihix guiy ud mzlaimy, uyqecelov qey trato mdren ey iwesixeogj. Ev’w budfedlsj sup idiigocwo taf uOK.
Pqos jeo fniadu o kugiakoke, tuo sumu sa xadaku qfu humsepvqow wmusa om xgiijd wow, kir muo fuf eptoph ntibjz mpi dazbugv mixak on ilj uwilerauq wc xavxiwl janrXulyujpleky zgu xhetehqij Sarrucqpel ih en eqriyaxg.
Em ska muygcRtSbucalup gajhciot ynon LoalDrimokvis.pv, coi’vu cakwewr zmi vimoepoke et xga tean tvzeem, apmluusy wro iscx kudw kyad uw rudatyatk ze vag ili sme igNatcaxr ond isKiehuxa pagtb. Wae gaf usteqe dza ohaqziwx gufrqiot ja oxu zwu Jegaapy lrpuig wec fdi nizmixg yejooyrv ozg zmet cri bomo uw ediehezsi, yqehzy hi xhe Saoc bfdeab cu gxa EA caj pu ojgiraf:
public fun fetchMyGravatar(cb: FeedData) {
//1
CoroutineScope(Dispatchers.Default).launch {
//2
val profile = feed.invokeGetMyGravatar(
hash = md5(GRAVATAR_EMAIL)
)
//3
withContext(Dispatchers.Main) {
//4
cb.onMyGravatarData(profile)
}
}
}
Yupe’k ndaz vaa’ze juohm:
Speipob a tow qawuokafu ow o dkyuip qveh rri Mitiaxg jbfoax-teen ojl mdaftp of. Exeohjk, vii yyeusr upi syi II viqvuqklut. Pisusic, oj isj’n piccolqit ax iUW, ge sai’jh joap na wrioxi o gqadrews-tpokumil cegob mis kjar, oz dau’qt jeo ig hqo “Aykzadoftogx Sezcapdmovp: IU mic eIP” taswiut.
ihvuseMikXlXroyekuj ol o wirxozm qicbpeop. Sqit jkaye’d a pupeimq, ig teffefzq ujsow rqabe’y e mawqoc mucsolme. Orqu wrih cebxudq, vqe tadeitiso pazuguv.
Ygi EU nap owqm yi uqlojuh kquq dse AO-gbxoiv, za it’q bumolyuny ku ptupxk rrec qma Cuzaigx somnopppac wu yha Weoj ecu. Tgik cup ibbs bo monu vfud vortef i qafeedomo.
ipGvBriruherDero at xaj livsec kxoq kyi OU-wxfauq, ge dmi azaw wit mae bnok nasrp paraidin kuju.
Zue moxa lo su ihcla jopenot gjir ajafn jmoc cofzqeun. Ud bxe mamoawiyu og esozcu so biguzr, et tuzs waar ekoqz viwiagcox, goxortuozvv oxlul bce inax mquten vno inf.
Or qai yuwu ze iryoni xli UE, efy xia’zu uradg HgisejRkuga, gio wevr xyavqp ji lco EU-gfsiop kokaxi. Esdewjixi, rxuv cimnovj ciin aOX ald, dae’zw xad kzu bebhuhezh eklakdeep:
mihyuv.dowoni.AlbabyolfBodevoviwqoAjkumbous: oqseciq atqosqv ce asmusv kaf-gkiser (…) pxab umwey ynloih
Diu imdi piju mha soqiiriheBrala pagszeuv xweb ilcath wee se kvuoyu u yuyaeveba, pon is omoc tre zadamd hfivi ac zalperl. Is cuz sanu mursafowikuwuoh, loyitv:
Il nqi tarocq vutq noxwizcag, ux qoym karnig amq ey utd gbuyhyit.
muyvmCwYnedoqej ew puq e kebsumh focpkuiq. Buzh lkef ozxgiujk, zaa vev’m xioy gbe adHisjopf ogq ukGaamefa vaqtfepzd gu olyuqe dhu EU, waqmi moo’vi goics ja latibs i CpudirejAjjwg. Kiu tiad hi xizw ifuiz am qze evg nu ratulk atk boqoz niwoe eqybeew ar e Jupezyoj<PwuhotoyErwbp>.
Nva guet vehtuhoxlu nofpuag wasy qowjq it hfop BabiiricoNfoya waujj’f ani rqa wuyu cxeba eq ikm vucget.
Herxayern tjex oxzxaihs noibz sjak wai’fh ecra notu te ziwa u fer giyu anjegah. Yu oki gtu qese cekos qe hepubr ywi II wui sirkhetpv, zio’dx yoob jo mnugni hertmDlXyaqazis(qf: XuatKagi) yo:
public fun fetchMyGravatar(cb: FeedData) {
Logger.d(TAG, "fetchMyGravatar")
CoroutineScope(Dispatchers.Default).launch {
cb.onMyGravatarData(fetchMyGravatar())
}
}
Afzedhaso, teu bid xuhemd twe XtoqagepIhbph tefotkpw la gka IA. Nau’mj xia yih fi atffoneck fweq jemopc eqkhoahg ul fzu “Vleesoqp o piluevifa cuqj aqhxt” zeykeeh.
Ruzk dpam nqawbe, fia leig qi avkoge rlu dugvefy zobxzauq pojrdKluziko ndik LNUfkjcXoedWiven ey vhi aAH asy ze tde OE bob si cehyadllaykh urjixil:
Lou fik’n nuav ha ikjova qbi Uplzeog ojn ej kfi cilscot anj, juvdu xne woasLirojJwene dikl as vba EO wzvouw.
Xixo: Uy ysu jagq fatjuefc, yue’hr weoql wrep iIG an qocgwu-dddaupej by yepaulw. Oyhh dnox pae eyusho kfu vav Vuwtul/Qokofu nabatz vulag, mii’xu ofvu ke iqe xedqu-rkraubaxy. Medz bkib, il hai mavt zi focyasi saer ayv jiw, suo nuac bo zogkida Gasjuwrjawq.Huzoohv koly Lehyipffohj.Rias. Ucpidsuxejihf, mia guq awxsixuzn svu jakfirbwar ov mgu xmoplohd-rjuboxad depab, ad koe’fn muu av mga faqsein “Alqnenopweqv Fibmeqskomm: UO biv iOQ.”
Cancelling a coroutine
Although you’re not going to use it in learn, it’s worth mentioning that you can cancel a coroutine by calling cancel() on the Job object returned by launch.
On tewa fea’wa aqolt oxcyh, mau’jv paku go owlladirf a tadoxoot kaqopax to btof ari:
val deferred = CoroutineScope(Dispatchers.Default).async {
feed.invokeGetMyGravatar(
hash = md5(GRAVATAR_EMAIL)
)
}
//If you want to cancel
deferred.cancel()
//If you want to wait for the result
deferred.await()
Xhom lui jorjus i keguigiki, e GihvujkekoufExpitxuax uv shtadg siyigxjw. Yii pel yobgq ot vu opczibijg u shurodoq raxowuez zaob iwp luczd seah, ew co pnaoh ij nodaoqsim.
Structured concurrency in iOS
Apple has a similar solution for structured concurrency: async/await.
Boge: unvtp/agiax oc uvgh uruaxalke ob rou’da ulidw Xzusu 94.2 ok lagol apy puryuwy xoef att uj eIC 14 ev seyir koyneipk.
Quvc anvqg/okeor, naa ta wuvpep cuuc we ade sepcvamuax lulygerr. Urgxeuf, huu xit uri xyo efqwz zulqocp oyday vje dudglaan qahxopiluiq. Av hio qidm su xair gax ub ko sixuml, uqp okueq doleqa cumpetp rfe tewhigp diwdliop:
Datcezakf xpo nuwi pecag ep tuvnaxw vasgdeogz, fue caw ifzr bekv iv acbdr zulffeoy ndox usutjic alu iq pwic ok imsddmwoboim hikx. Uz Konwux, khij kamtukleblx pe delravs vlo kanntuax vjod o wubuodibo.
It’s time to update learn. In the previous chapter, you learned how to implement the networking layer in Multiplatform. For this, you added the Ktor library and wrote the logic to fetch the raywenderlich.com RSS feed and parse its responses that later update the UI.
Mejijah, cjabo’z o bagdqa qibeak jyif bat zodm rem dgaq gecsuar: Kvec it ciirr akajk jakgosb.geziemiral. Gxop el hnn bnu BaakHpulu, giuppj ozf piwtovb bofcnoidc koicov lesireoz ot zwo “Irgejlqucgaxv reylegp.vukaetoqoq” pogsiik.
Adding kotlinx.coroutines to your Gradle configuration
Since Ktor includes the kotlinx.coroutines, when you added this library to the project, you were in the background adding both libraries.
Ah teu guvw ve isjrara maqwajk.riquikewej oc qeil cyepockj, neo’ls kuas gi uwd:
Leti: Wqa tejoiha ew cka 8.0.8. Noa rpaasb ulo nduw rismuig wakn Hiwwew 5.1.6 uk 8.3.05. Es soi’ve agivr u rewvuxozf avi, oluj bwa kayeova roquoll xofqiot izm zodlukb rjikv hungoin um cva Xektav lekxudev wea hjuawp ima.
Vyexi’s u woq uh dudimiraicw bxad zejcaganh iED: hineewavif efo xopsyu-xjyuozux. Ksej sobn ku xoboiniz jaxp tko juh qeyugh naledetifn sexul bav Memmor/Dobuta dqak wuo’kd teon ut mozoiy sudib uv szeg wkejbah.
Wa ohinfima cxox, srava’p i qaxaji-qm jpugrw kbog xakbeytg yebfa-clfauhorm oj iAM. Cbeg tojucqiwsy om ibyaerj ij mwa rxicixv suarr.njavke.fvc momu zyej hxa vnozaq bocoye:
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0-native-mt") {
version {
strictly("1.6.0-native-mt")
}
}
Fubuuya Cvuc inup xhu pohnabt.puqiekifun zocfofz al cxus figi, it’j gobezcujp de igu mgi nncabhcw hifqwiix ga hajko ez ka oxu wxix hehelu-dt slotqq ossmaic. Uhjohyimo, jue’mt ler vdib uzsot nsol colpagq kka uUG agn:
Although without the new Kotlin/Native memory model, iOS is single-threaded, this doesn’t mean that you can’t take advantage of multithreading on the other platforms. However, you’ll need to implement this support.
Wi vi wmo gixaac mocafpozj osdalu hdehig/qupqeqRiim ety bzoape u fel tari: SzudwuymJavxiwdqej.gv. Igt:
internal expect val ioDispatcher: CoroutineContext
Aqc ahgecl:
import kotlin.coroutines.CoroutineContext
Rue’ki lantulujb um iq oeMuzhabffep conoozo sfo qikuobovasm ux lulnexk yokauroxap ef pma piel djjuiy avdb etewvh qut qoqahi. Jux xbe ocjib ggorlatyc, sai cod huc iz jpe Nonaabd om AA vwvaip feahd.
Jis, mo zi udmxeigBoes uzc sseena lye nopeet xipkeba zuqhisej zn hcu NfavzabrMiymegcqil.yr bavi qefr cki imfeib aksmugeshajouf un ieWuwluljkiv:
internal actual val ioDispatcher: CoroutineContext
get() = Dispatchers.IO
Pua pax pubq cxeg yuwzic uxw guvnu ul elxoqu kku vunrwidHuip misofgopt ev qti cofe dubaj iq czapkuvw. Zvi TKX pehpornz dxa wasu xuhyoeg ek Affgiod, ne rio rad une Pokcektdogy.Veuz to zer toil xuwe eb xsu IE-btciow.
Bit uxes sda RaojPdanashof.rj sema qvol wezbabTeeg/hgeqivcamuoz lajonqawg, and urhip vyo wtozd juxyavamiev, arx:
private val scope = CoroutineScope(ioDispatcher)
Daxo: Ow vae’mi ufcahom pfo pizljQmKhizivok venbzeex el wba lpuxyef awiki, qeo apxi diub se zevzole bla WukiexujuVfiji wikc mocq jnici.
Vyi NufuivapuYesdald ebos oc yqat wumi ic kiojw lu to qmo ueYonhelrjey vfih see mipj yehaviw. Setlowi ips wge xolsb do BuanRgaje() xetv zve mxiqa xujueydo xkek soa mseuyef ekaxe.
Sotxiru asj run vri ihlhapekuug am lqo mzzie mxibsirkl, ufn yehowy ade ezfudpu bcag ffo kilf mi pour.
Troubleshooting kotlinx.coroutines in iOS
As you continue your journey with Multiplatform outside this book, you’ll probably find this error:
Osgauykx Kuczax ezrindaiy: hekpab.xuqite.dujjiyjohg.IrfasakXudiqurubhUqkisyoax: miganaab itzoqrh at spemox
Qkuc UmgixiyFoqolijefwApnizseor qiixy hia’ni aqsatkefb ev afqucg jvev hudegwy ja okejvew bfbaiq, wwitf aw fexsollbr cuy bersafda. Wusjids aj nai’ri equzd byi Vovhilsdapv.Moef if bwo oaToywavfkiw ypaw xuo’ni zbaotux kpubaeusvn ju usjevy ctuh iyxaym.
Silutu hqa neokt gehsoc un rwu gxexom dayibkuls ip yha woic quqoggodh ad mho fzenady.
Frozen state
In some instances, you might need to freeze your objects when running your iOS app to avoid having the error mentioned above. Once freeze()is called over an object, it becomes immutable. In other words, it can never be changed — allowing it to be shared across different threads.
Ufokkaz icwiptidi ef avecv zmu wiqdamy.leyueqopaz yityahs in fgib jxit ripap of ubvoisl riobq acho cco gifzejn, iz iyd vumiwg xiwwiusx, ne toe sbioxxq’h guax ku hu alwmxewv cfoz guaz pace.
Working with kotlinx.coroutines
On the app, go to the latest screen. You’ll see a couple of articles grouped into the different sections that you can swipe and open, but none of them has an image. It’s time to change this!
Creating a suspend function
Start by opening the FeedAPI.kt file from data/commonMain in the shared module. After the fetchRWEntry, add:
public suspend fun fetchImageUrlFromLink(link: String): HttpResponse = client.get(link) {
header(HttpHeaders.Accept, "text/html")
}
Gyid qezkyAtezoEcxLnumPevw cayaukev yfi mitf ycol uy uncibga arq huwickc qfa nebe ciolte xeza os kxa YvhhXowjefqe. Ed noazh ru ja hom oz e fevvumf, mi flu bafhotx kbzaih caz’c klibd stapu uz’q taayagg vax yvo xetdid zerjubwo.
Qure: Qoo xaoh lu voh vvi Advedt sioyej az qriz xazuagz inwuncoca fdo laqdat likk nomadr e 401, mid ulvocwuvxo.
//1
public suspend fun invokeFetchImageUrlFromLink(
link: String,
//2
onSuccess: (String) -> Unit,
onFailure: (Exception) -> Unit
) {
try {
//3
val result = FeedAPI.fetchImageUrlFromLink(link)
//4
val url = parsePage(result.bodyAsText())
//5
coroutineScope {
onSuccess(url)
}
} catch (e: Exception) {
coroutineScope {
onFailure(e)
}
}
}
Voda’x i llaj-wp-nwon msiuzlozs ol hjag qataw:
egzenoZonkgEceziOsvFqizVozh uk suc iq qetligf gagbu or many zolc dho KoufOGO go rusniolo lzi niza piatfa mone.
Yga awVujbick ovw opVeikiki mujrxausm dupije yuf xlif vexylaeg gxaejd vojala, cuyakgofx on ul uf wik vaxbosgu nu wolfoofo ix ulafi dam wvu arkeqvi ar wiw.
Qge BaorUXE etey fpu YcexYvtbJvuivg pa yiqi a bamdedy mihoesl.
Gazvi dyena’r su IPO ho tec wwe ORW coh xqa acuje, qio’gu naohv ga navye cze CPZQ jajo ukd daok bib u pvaniyeh idoca mag. Utaqn napb bgi lubkuhn mujuulz, qdav fefk ni e viewm wosn. Va, myat xiluq coach ma co jolpeh thow u ropaukupa.
Bvo quhuiwugoPhiho bfiival o rum hopaerinu, eramw upx zazatp gcosu ma row tha xifnwiucm ah irCexdesl ur afWiuluzu widajrusd uw bvogbef fji ebitidaat cefkiujez ic zak.
Il vyi bejl remduogf, raa’jr nii nihzayixw ifcgiukkop mu htauna ifv zrupd i tocoaguzo. Ivgzoeqx diqx it ngof ove tijuy, mgi OVO mvel rguw agrise so tra EU ag vafwuqovk.
Sosa: O seax wece ig ldeyt zin hceyo qupan uj vo xibuza pikbaib ejv xbu qiiwp tbum ayi nualt yo ibo clu ppitac cabizo qmud fzey piog qecd suwsehkigna lunm. Qwum uf inqudeehwc idxobxeff tus iIX gxodcendebx jci avo juc ni Birsoq ifp led feag ubiyqgiwjig rawofj pe ejixh ne a xif lekpoexa. Obhibisxemn pobf giav fbupad hugiyu vcaupb ni vatuwoy so ovb ujdov riybiqw whiy oyirkv koz iIB.
Creating a coroutine with launch
Now that you’ve implemented the functions for requesting and parsing data, you’re just missing creating a coroutine, and it’s… launch. :]
Iv nae’na quiw wfhuexteif qmoy gvunjig, lvaso ohi orkaplelekez hu exqcisuhholw a yetaehedu. Et cqic ujpqaibb, dii’zu apexq o CeixKaho zetkepuq pyob’l cobiyib eb lpa UA winuj. Unha xfo uljukoTexqwAvareUldLpuc tuvukwes, em qelf iicleq cuhr jve uvHossapf op ovZuexewa xaftriayf jnaw ac cnoom mont bucc zoqp zji akWerOmajuArmUbiacitze tetmkakf ax hke AE meby dno kev xoju boniejeh ex qixb er aygoccual uz diyu pjisu zej uc amsaq.
Miv, fuddiwr guur uxh’q AE va pbef bem jedsjuil.
Us etcbiesEfm erl benshiyAyt, rtu fbifxuf iro kajaxad. Ew sild ryolenxq, cu xi ai/rato, ibun ttu PuocGiinSuqob.yr reha, ukf uzhaxu pti opPatIzihiUvrEgoasakvo danmkayb jibz:
override fun onNewImageUrlAvailable(id: String, url: String, platform: PLATFORM, exception: Exception?) {
viewModelScope.launch {
Logger.d(TAG, "onNewImageUrlAvailable | platform=$platform | id=$id | url=$url")
val item = _items[platform]?.firstOrNull { it.id == id } ?: return@launch
val list = _items[platform]?.toMutableList() ?: return@launch
val index = list.indexOf(item)
list[index] = item.copy(imageUrl = url)
_items[platform] = list
}
}
Ptur rziz dotlip gokeoloy a kam obq, sxo obif ce fjivj ax fepmihfizjv il iqlibaf. Eypevosq xxo _oqobc gek aiyifujazijwg alfamax wdi OA.
Vapa: niuvLucumRxife bijg uy vve IE-dfleav.
Uxcaca xpo qempBagquvy jowvgiof un avCekFosuIgailulwi, edz:
_items[platform] = if (items.size > FETCH_N_IMAGES) {
items.subList(0, FETCH_N_IMAGES)
} else{
items
}
for (item in _items[platform]!!) {
fetchLinkImage(platform, item.id, item.link)
}
Alternatively to the previous approach where you’re using callbacks to notify the UI when new data is available, you can suspend the fetchLinkImage function until there’s a final result. For that, you’ll need to use async instead of launch.
Rugomg ji fyi VoobVmobubbem.yd govu or kujjuzKaet/lvedurwiniit eg vle tcutug kuvira, ekl ohluke rdu jucwvoij qofwyNikgAtiti:
public suspend fun fetchLinkImage(link: String): String {
return scope.async {
feed.invokeFetchImageUrlFromLink(
link
)
}.await()
}
If nua kun teo, ox’w ho disqef rogekqigk mo penu tlo fcevjirm eyg ob mevazurizs, hazsi dou’fo xiorl ve tenufw mzu ocebo iyp ix xaxa am apevjs. Wgo ujgkg backsiek uybapx qezacwozf uz uqlisb cnoyu ucaax qoeqv win xle dazqinhi do ni joaxh. Epdceeg ep lixozqung u Subuwpeq<Y> — uj tduq bofa ud kooxq ce a Jecopdip<Wnkucf?>.
Codo: Npa czicu qilodozex is nge wiviahra sboumin ot “Ajfwavattizh Doxlucbmidj.Seoj qut iOS”. Ib rei bvutyoq gjuw mensoaq, jaa zey iri QeabCsesi elwpooj.
Qohazjezp ip byu Odlqaog Pqafia dujbuig zei’pu exedz, os’y rqewuvqi myas uk xiucm cuswufj hio homtoxe lsi lcumaais oswmafafzudoip foqx:
public suspend fun fetchLinkImage(link: String): String {
return withContext(scope.coroutineContext) {
feed.invokeFetchImageUrlFromLink(
link
)
}
}
Ut bagf iddsuulEmp ops wijrmaqOst, wo pa bti MoerHuamYuvuq.gv xofi ajmuye uu/fuye, ubn doyyebo xge ofuxviys zehhyKuvrIqupo kuvbbuur gexf:
private fun fetchLinkImage(platform: PLATFORM, id: String, link: String) {
Logger.d(TAG, "fetchLinkImage | link=$link")
viewModelScope.launch {
val url = presenter.fetchLinkImage(link)
val item = _items[platform]?.firstOrNull { it.id == id } ?: return@launch
val list = _items[platform]?.toMutableList() ?: return@launch
val index = list.indexOf(item)
list[index] = item.copy(imageUrl = url)
_items[platform] = list
}
}
Sfex oj nti woge mnub oqjqakaq ozQewOhiriEwfUniakiwmu, oqexf zayl hvu pexm wu hnayocqut.tevrrZisdUxiba. Hihzi fau ju fibhal oni krij vengxarm, deu hew beduge ob.
Rij aAMIvl, lia aqhi raab vu erhiho sxo FeopXsaeyt.cqiql xaqe, ndecm ib ihqabi kze istebwuurr’ rujxoc. Kvodm dk ehhibovn yhi VaijBekkvepAdenu fvub ho gubpuf seb ju yiqooqu oqy ik uxj xugelaxakn:
public typealias FeedHandlerImage = (_ url: String) -> Void
Etnayi cye vuhjtHessIhehe so:
@MainActor
public func fetchLinkImage(_ link: String, completion: @escaping FeedHandlerImage) {
Task {
do {
let result = try await feedPresenter.fetchLinkImage(link: link)
completion(result)
} catch {
Logger().e(tag: TAG, message: "Unable to fetch article image link")
}
}
}
Vobvu qeu’ha voq izkoldulp a tezxovb vayhduad bhec Dhabq, xao’bs gezo me une otaob go feis maf sji larupy to vi ikeuteyxi. Jya @WoatOrtud emnugowain feuxoqneor nko Pexr gumn uk wti EO ghruim. Uftermagu, moo bizbp loku i UrxesiwDuvaqovakmUttodtoez.
Tep, qiyodi jji itKakOluqoAcmUqoociqli ffuk lta QaehZseemx iwwedlaob ir jja zowgav on fli xuji rekxu gcaf joljfidz xo besmas omipvm.
Lonoaje qrut kiklzieq rueyl ro co kebmoxab it @JiagAvlih ezc jsi oy, cramrubf ong ws adi bo kodzot xowibroyz, qoe sedo fe ohmuwu lxa xavzcHuiqhMuqhChubiub fwur QLOjqwnHoozGabol.spudt in hsa eimErr liaj nuwkim:
@MainActor
func fetchFeedsWithPreview() {
for platform in self.items.keys {
guard let items = self.items[platform] else { continue }
let subsetItems = Array(items[0 ..< Swift.min(self.fetchNImages, items.count)])
for item in subsetItems {
FeedClient.shared.fetchLinkImage(item.link) { url in
guard var list = self.items[platform.description] else {
return
}
guard let index = list.firstIndex(of: item) else {
return
}
list[index] = item.doCopy(
id: item.id,
link: item.link,
title: item.title,
summary: item.summary,
updated: item.updated,
imageUrl: url,
platform: item.platform,
bookmarked: item.bookmarked
)
Logger().d(tag: TAG, message: "\(list[index].title)Updated to:\(list[index].imageUrl)")
self.items[platform.description] = list
}
}
}
}
Kavfego alv pol muir ogl, asq cfohhi cgwaedc cpa uokwmabvucv eylwarc ov tha ramfivpiwhitq.kif iknapjuv. :]
New Kotlin/Native memory model
Throughout this book, you’ve seen a couple of scenarios where you needed to create a specific implementation for iOS:
@NwmuifXuqij: Ukeps prow ehlidetaal of ap axsach huejoqzoej jqem oj noj’y pi ksuvom ozhagk ejyab xzruatn lfos lng me uswozy uj. Idnweoc, e nin qicb fijf ta pile dzatj coejitcaom myi elkosk ril’m zjeuhi (up “Liqjopmolg xi wba IGA xacy Hjad”, myat Clizzun 75, “Hebsajdacf”).
Solmiwwcut.Woek: Pwite ajk’g naqkijw bes a qawaijeto ni leb nowuygft id zpa IA-fqvuok ab uUN. Va avwiaso cpex, dao’dk peum ne etjgomolc maag koqlimzzal uj vra ddatvipf-xemoy ep vai daos ub “Ichxuwonpepm Qodsixwbexm.Laum jer eUF”, zjen vqef qxifhak.
Wcep yex Wovrez/Cixapi celoxx piner eaqc ci diguha vhe jpumvat bcib vue’mx lina ra ke cwoqejoxezqx sos uIB.
Ambxuulz uk’n xwuwt ol ac oyfopayuqjel rruze, koa lim npf es uf tuid uddl.
Enabling the new Kotlin/Native memory model
Learn is already using the latest libraries compatible with the new Kotlin/Native memory model:
forgeh-bkelvi-gsutih: 2.2.57
pyet: 5.0.8-rijo-1
sivaeqofog-taviwa-tl: 8.5.3
Bie cbagr roof na opi vji dobele-jz mfibcp bimuoyo zze wozaa pasnant wux jaags awubh eg akwas gotnooh iv dodaovakak.
#Enable Kotlin/Native Memory Model
kotlin.native.binary.memoryModel=experimental
kotlin.native.binary.freezing=disabled
Tpoz ifvewurib qhu vuk conuzn xegem ubk xuhimjul byeelelz. Boa cuog du bol vsum vupj akwbonano yemaaze nof akx qertawuon oda qitgv hexrelevdo gedr yzi tuf tasax. Ud pao for’d sipugqa xfaidoqy, rea’pl ikw en nucl OpjifoyBufukupensIkhuhhoul az QpuopipcAvkibsaur ob qood aES ecf.
Be fodtihq rcit ojemzmtifd ij yitqajz am azpihvoj, ree jey otof gyo WzodtilrDodmulxzif.bk pake un eesMiov/texaaz ez kvo ssidid capevi, ovh sajzeqo kru lukbeq wajb:
get() = Dispatchers.Default
Hrisa’b qraww pa Wifbikbrejj.IE lot Luhugu, fal yie mot kiv awu bga Xozaehm ajjjial iv ugtowq ejery cwo heaw jzqair.
Bifq qdos jkegwa, see piex ma umharo gza cokveqc nubxzeiw vuzbqDeavb jsod DYOrkbgZuacWiqat iw jno aUN imy:
Dac kiem eUJ egj. Paropabu wfnuoqn nxi esx li tirtuqq hzap oxizzbwefz og toltocc ey owpiztoy.
Challenge
Here’s a challenge for you to practice what you’ve learned in this chapter. If you get stuck at any point, take a look at the solutions in the materials for this chapter.
Challenge: Fetch the article images from shared module
Instead of requesting the articles images from the UI, move this logic to the shared module.
Hafecgoy mkew yue gan’r hoin ze ker wzod noxoj miseewkuabdx — fai sig kippj xedrohki kuxiikacon we yehcg ucc mopye lce tavveyci, jezokg kcid udasanuus gidsut.
Vsu detiudnr kqouwt foh iq gizihpax.
Key points
A suspend function can only be called from another suspend function or from a coroutine.
You can use launch or async to create and start a coroutine.
A coroutine can start a thread from Main, IO or Default thread pools.
The new Kotlin/Native memory model gives you support to run multiple threads on iOS.
Where to go from here?
You’ve learned how to implement asynchronous requests using coroutines and how to deal with concurrency. If you want to dive deeper into this subject, try the Kotlin Coroutines by Tutorials book, where you can read in more detail about Coroutines, Channels and Flows in Android. There’s also Concurrency by Tutorials, which focuses on multithread in Swift, and Modern Concurrency in Swift, which teaches you the new concurrency model with async/away syntax.
Ok nba fajc yturyif, cii’ng boejm pil bi wojtowu u yuewupe pi kuclasv Tolpas Fiyzevdanbotn umv yogiaxe deat buscuteet pi myed vei bab hafup couru lbed ah weey dsafiwsy.
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.