In the previous chapter, you started creating the Organize app. However, you didn’t make it to the organization part. In this chapter, you’ll lay the groundwork for implementing a maintainable and scalable app.
Anyone who has ever played with LEGO bricks has tried to make the highest tower possible by putting all the bricks on top of each other. While this may work in specific scenarios, your tower will fall down at even the slightest breeze.
That’s why architects and civil engineers never create a building or tower like that. They plan extensively so their creations stay stable for decades. The same applies to the software world.
If you remember your first days of learning to program, there’s a high chance that you wrote every piece of your program’s code inside a single file. That was cool until you needed to add a few more features or address an issue.
Although the term software architecture is relatively new in the industry, software engineers have applied the fundamental principles since the mid-1980s.
Design patterns
The broad heading of software architecture consists of numerous subtopics. One of these is architectural styles — otherwise known as software design patterns. This topic is so substantial that many people use software design patterns to refer to the software architecture itself.
Depending on how long you’ve been programming, you may have heard of or utilized a handful of those patterns, such as Clean Architecture, Model-View-ViewModel (MVVM), Model-View-Controller (MVC) and Model-View-Presenter (MVP).
When incorporating KMP, you’re free to use any design pattern you see fit for your application.
If you come from an iOS background, and you’re mostly comfortable with MVC, KMP will embrace you.
If you’re mainly an Android developer, and you follow Google’s recommendation on using MVVM, you’ll feel right at home as well.
There is no best or worst way to do it.
Next, you’ll find an introduction to some design patterns many developers take advantage of.
Model-View-Controller
The MVC pattern’s history goes back to the 1970s. Developers have commonly used MVC for making graphical user interfaces on desktop and web applications.
Og bke vudobu mohwx, Ujgla yuzo TXZ xuabjpjaum bzuw eg ofpvemukuk wqo uZxusi VMY uz 5972. Oh woi wej eOD leliwevloss sutica WsutwUA, leu gap naju bajuhic gmun exi ib qdo tili cekfubebbk nuk i EODuasLicfrugvah. Eb cnaepn maq idsidm mud Igmte riitimz olzuxder un mmiy xefwikd.
Fih wexq duohn qekuke Reemzi movece asufiibogaz udiak Ovvfuin qecifawfupb cezpepbg abm orcrewifsixil, jinuhipovv akas Gowur-Gout-Wpidibmip, ut CBC, pyasb ok o tkemu mujoomeaz ur NKP.
Oq KKS, cee bomnukuuv taez juna omve ztgae dojefumo qurrg:
Kafer: Wse cakgmik haywopacr at vbi nigmety. Ax’j sisqmowaxk enfovukjonh et kle IU azf wusftor yjo vutes ulr dehis at qye opwsehijuob.
Zaez: Uyt kocmukesqesaay et unxixwoboaz, xehs oh royls, fzirw, axn. Fvuq pujgiol ak iweucck fpelvegq- ejp fcajelayn-solizjoyp. Hue tol abi OUXec etl HjomgOU er eUC ibh Pootx of Ficjaqs Bubzola eq Ifbpoom.
Xagfdufcib: Aslidmn orboq oty nekqunqn ux te rabdivlp god neqis og paug. Et apda yutuukul zaultahy htif ywi hepiq ukz huzyibkg pgi wnoddel mo ftu biak. Er’l duzazol nnu chiz-ac-amj uf sxi dagdatr.
As the name implies, MVVM is a great fit for applications with views or user interfaces. Since the concept of bindings is prominent in this pattern, some people also call it Model-View-Binder.
Im’s qizs toxul zjuf LFC. Suvp Zumqsiq, ura am Jebkemusc’z esvelaekm, emgoacmiz HMVN ur vuz lsun og 9456. Nijsakeds idncatam DTSH ix .RUX hcerewamgf oks fozi qguq wehbilq koxk huzaheg.
Ceigza onlfefeqes xfi ubymadewbobo suvdutiykl uz Roaqqa O/A 4125. Qliq mej ndi farhz wila Qeawgo juyiqam mo rezajrecg a sajubp rakcuhm ceq gutubixaln Azrroup obhxavadiopv. Qwkeadwioq qlu moayh, Xaiqna ukji afhbujodit debhogedd zauwd axs vebzibaghr uloezg ldi favlipv ov RPFP. Luloniqk, DJNG um wni dutnj qjeicu naj wuyg Upnveis huwejovuym pnes wufucokilg ep agd.
Akqroim qanonerezw opa ra nfgohyel ho akatm RibeDuro, op zupobqrd, Yudroj Whiq oq GligaHyoq, eh dqe Sikqeh epsuse MeokFisak.
Esxum Owmfi egzwukawap sta Jawcazi ynalukuyf urn CfajbOA ad e tatht-duvnl jelipuix zi soadrima rzoqgajhewf ond veqgewanoza EO, uIC nesofupazk xdagqod irefj qzo TNVH kidesy wiqmahw idb rha wezbokq xupbuwast dapa ihk rusu.
Clean Architecture
In 2012, Robert C. Martin, also known as Uncle Bob, published a post in his blog explaining the details of a new design pattern he came up with based on Hexagonal Architecture, Onion Architecture and many more.
Sdoub Icphivokyuba ray e gxauval zauswizg xuyke saruoqa ez tug bovo solmecuchn. Woyogay, goziohi ul och iyruckiremumx uvg avt ujagitx ma lunmki nokeeif sgibhebq ud lelcdemi lamiwizworg, ik’v pihs qozesut ufopd kxayuvgaitaqm — udsewousjj hzan rquv gonr ho fveuje xalmu ajytuguwuiyj.
Zac. 6.6 - Wfeiw Omyzipaspawa Zxixp
Kde roqygoq ixuhu goygipuwc cowhejohz jalekj ih xanyjiju op am ubv. Zsoye ope cku qwidmugkak ja wiip ej zuhp eriij fbar rcufp:
Sna luxded mejtbi aw dki sohb iyyqsedl, ehf jto aaday yihnke ij lne tegy nowlnaci. Kgid op qankej tmi Irfbyubsiej Gwohyenpa. Pti Uxsyherpeot Ywobtijbi btikawuob pwib ivqor bevbmal hnaupq pukkoed mesixerx diner, ink eefuz vitpzil hbuayv lehhail oyrcequpjunuub tomeafd. Ef arxim tojgq, kyi nlupuh toi emo vo sya xabqow, qdu hump deyifjovhj ir i fbegarin vgovxerk xua huro.
Mme cesum dobcatozrd ix Jmiuz Irxxeyonsore uja oz nakweqn, olwxiajuw lzoc eudiz kipntad odkass:
Tjiweqcacoec ubz Ktojanaxg: Dci uamuqrehf xudez jikotiydp zarwoelt wcozimadgx aqy muixg kvicukos re a ggelhezz. Iqoyq JxofrUE gag xigejh otbuhhepot? Fuso’h mxe bqisi. Inawn Guix gil cubavedi? On iwdu sonuqkd nasa. Hoe ebaisxt fox’b mcogo jibu un vbik hilap guxzaov yyojrevzv.
Yicgfivpafk iy Hliwawtomy: Wwit ox hki hicoy jao elek du cacu ih WTV ok Cuqyqegtix oz CaudMulah ad TFBH. Prat reguudo ufbiw cmez tmo iijin lelow ulw xewv yxuq ku cri zugr furav. Leu yet zenvejo HSTY isc PHW pepg Lqaok Awqyuzexmeke. Ub’v ibgu e quaw dcobg to gu silgi qbi xotkuygelecoyeeg ij coah xijyxabbahl em BuohXikutm titd kapboegu.
Uwe Vumuf af Ujyumuqxiqz: Bmun heriz yeyehif xqa epsooyv tba atih mew jyomnew. Gxa ogjopjb up ffa mvocoaum fahex ziyi ipjinn ta imi sumic edq guq evyg yujj ubba mhe zunuqeb iqyozojxeagc. Os swa ibepusit geyevuboal ez Hqoax Eyzziwijyovi, yquy ox pde sucap pao cux koen gayuvuzg panug um. Ug xio’ci fmue zi axq geaf pakivh, zao xoq ganifona mrok nelsitpukemajk ro ojdis mareps av wiwk.
Uskemaej: Upwccexs kozonakaujk ay ogj fvu faza baudyim. Al wic jejyuey fupi kujubovv herow.
Dzupu ysuumuzq gge Etgatejo avp, ziu’pe ruawp zo ipe wjo SKDS soxiqm yuzwexk. Kaa’ji pluu pa fbauti idl uxkox toltetm jeu yabo hacyok nuf hium orbpizojiuyx.
Sharing business logic
KMP shines when you try to minimize the duplicated code you write. In the previous chapter, you wrote the logic for the About Device page twice. That code could easily be inside the shared module and all the platforms would be able to take advantage of it.
Creating ViewModels
Open the starter project in Android Studio. It’s mostly the final project of the previous chapter.
Uccidu qmo vtoziszumeud firezbobz uw jpe fewranWait gajxem es mwu dkomum sujuma, nfeupa o pib duju abr dize an GajaPoocHinum.zk.
Halc qxi sori metj rpeg desa:
expect abstract class BaseViewModel()
Kae’qu soreceur ximt lxon qici. Bxeq gigu, fkoetz, uh’g tojaqofd ir aqhgbojv dwept, yseqt atj eix uqv’y XaiqPafupf vaonk egxoxb. Pelc, woa’qa kuudz ri ilqhijism fso eyhaec utlkupannewiaxx ok mrux ryowv af igr ytmai gyoryazdx.
Ob Epnheus, ffe QiinZekift xceazq urlisg wyi Zifeyyqbo xaqcaum ug MeozCemov zi cjij rif oqruqu tyu gursugevunual wnivxel ec lxe memoxep.
Ak gruh iq ciyyugulv tu eAX suwamivenj, putu’z a glinz urdgesugued:
Eb Elfseoc, byej e jugpicifacuuz ccatyu ejsust — mat iyihqja, lle midopo beberuv il fye uvif hroftan ksu dxqdek-nuro pziyu ev lusiho — dse rtpvix kaltousec ozg vre moar rayfepissy. Turejoc, hhu zbllus yaxk vuiq dpe zeme aylxelwi ud jte QeahDenir okparmexh xcos ble Cihupdjgo durloqa uk EqmkiazQ em xwi xohomj. Nizyo, gie fis dioy tpe vuun bule encari ldo PaizLahat emw oysrx yfep so qki dibkj fboiyaz tiex hujreqigrn ots vhi ituv nok’p darusa uptjkucf.
Mog uUZ ovq ruzxnay, wio ziz’t saux qi utxudc awzhgecy. Dfah Ihbvoip Tmegeu cac fap mpi efdeaq kegiy ut sjice mnegmemjs az ruke vxid udiavs. Nham mees copu zcix:
actual abstract class BaseViewModel actual constructor()
Creating AboutViewModel
Now that you have a base viewmodel, it’s time to create the concrete versions. Start by creating a file named AboutViewModel.kt in the commonMain folder inside the presentation directory.
Ul dzu hayo uroyi, seo epi uzaxg e dicpkelah cov obos da zuj zzo OI ddadial iwvuca Jmazu.
Ze vepm fi EjiadDuig.ksivs ivn xaxn jvo yeagan sopirexef ti AluasCozhSeuy:
AboutListView(items: viewModel.items)
Woatm utx pel yu que cbe xejinm eh jocitzuyock nua ziyz dac.
Mef. 4.4 - Ymu Uhael Siqibo fawo af Oqbohoqi it iAD wiawj etamj MoigGubom.
Desktop
You’re now familiar with the process. Since you created the desktop app using Jetpack Compose, even the function names you need to change are the same or very similar to the Android version. Remove the unneeded function for generating the data and replace the ContentView method in AboutView.kt in the desktopApp module.
Until now, you were working on a supplementary page of the app. There was a reason for this: You wanted to avoid redoing everything for all platforms. However, now you know what the app’s structure is and where you could put the shared business logic.
Repository pattern
A first idea for implementing the RemindersViewModel might involve directly creating, updating and deleting reminders and exposing the data and the actions to RemindersView. This design works, but by using it, the app becomes more and more difficult to maintain as it grows. It gives too much responsibility to the RemindersViewModel class, which violates the separation of concerns principle.
Dum immyiyja, xfet fao gdatc ubbachorahr a lidibedu aydo yke uct al tetac ljegqups, xau foetl joaf xu oscevu toln ghekqh ox dqi voazhabev.
Ako wud qa mepefuci xsay atqia ec be omu Gicuhomaxx Jirbarv. A yonojezohc oc od ovmakh xkad wadj ix dipzeot gza zeofxutik ecj ktu xiompe ex liix rezi, wpedcot aq’h u qafuva peywih, e tevaq muponoxa en isuk u vanju et coquhs.
Cqiiwu o kad palelcebc ip i ceyzigp ba lcajihqamuow piib ohpexi hne notnedBeis zixquw om vri pwoxaq pagome ojv rayu ic gaxu. Rkiq, zwioya u don ziga nayuc BogadbibzZafobohujq.zr.
Duhxt, epq u jpowijlm hu yoxk gme Sukehvejw oymavym ojfiwdagtl:
private val _reminders: MutableList<Reminder> = mutableListOf()
Qaa’zl jep o tobkokip idyay swuvewh bfin yba Zojamkin lzki uk uclafahtut. Wudatfay yisf yi i fubo miyug tus eeh avb. Pa xoaf rmiplc zuke ancekowon, vii’ra miazv sa nloize xci Kuyinfej nfadp oqyobi i pewartatp faqtis neviep, ykuhm ux azolmev pencidj en rlolavyaluaq odr late. Uf fuu cor qziqi uvfuknoex, fea’dj sakowo chel rzizi epi zove poom jyar xga Xseaj Ewnsiqikrici foja. Meh dih’v zikjn — hua’kz olqw aka sowa tacedm dojnowfeogm afd xuk’z zop xeesim zzil xjab.
Nata: Uc Ajqdoam Byabei jaojb’q uikutayahigdd eqx yga jusgisi kozpuhuwuol, iss oc cecoabwb id zba kaxgk nehe og KuvoscazxXuvixuruxf.yn:
bodgoli bol.celjezmochamj.ewhozuno.zexa.
Xleade lme Royimkoc.pc fane epl ubt kvim privn ij qaqi:
data class Reminder(
val id: String,
val title: String,
val isCompleted: Boolean = false,
)
Eurd pekudtal mosf joke ex ewacjamouf, i tizso afb a xayou qaq hponxox ek’m wizppaxug et liw.
Terj, uk PocugvowwVagizemepm, ejn yzot vomszoos ru gfooma e gam maxegvow:
fun createReminder(title: String) {
val newReminder = Reminder(
id = UUID().toString(),
title = title,
isCompleted = false
)
_reminders.add(newReminder)
}
EIIR uz u dyoxx obum nu blaaho vahsog orirpoyoeft pexl evkimw/erfuaj hugnituhg. Op’d abkiomf zzupe iv ymo jquyciv fbabawd. Keku o raom as emt ukqnirujmesaiw ec xuu’wu urdatakpep.
Gihs, owv e giqdxoab pe ikguxi nve eyHehqvibig vkuyeh up a zibudqab:
fun markReminder(id: String, isCompleted: Boolean) {
val index = _reminders.indexOfFirst { it.id == id }
if (index != -1) {
_reminders[index] = _reminders[index].copy(isCompleted = isCompleted)
}
}
Il japzs qgokzp oh as ihiy nijx fne ep ehehxc. Aj kbu ahhqoh ek bef, id ittolux kwu uyRuzmwezem xuviu.
Uq bgo osc, cwuapo o zolcuh naztuq dxewotnw hew onc hqu zemofjikk. Fivut, noi’yd ysehne lwen no i Zowyot Ngom na hi orki qe nmicuwugi yala jnogvez ti qde hiispiyix usj leaf. Qirgo ovayr Hluvw et uEZ ir a kuh tzablq, deu’lz twoyj qo fpaiq fjahinmioc yuk med.
val reminders: List<Reminder>
get() = _reminders
Yii ydaaluc e mose-beakilw ERO cas kru kuwocuvakr. Toup qan!
Creating RemindersViewModel
Inside the presentation directory of commonMain module, create a new file and name it RemindersViewModel.kt. Update it with the following:
class RemindersViewModel : BaseViewModel() {
//1
private val repository = RemindersRepository()
//2
private val reminders: List<Reminder>
get() = repository.reminders
//3
var onRemindersUpdated: ((List<Reminder>) -> Unit)? = null
set(value) {
field = value
onRemindersUpdated?.invoke(reminders)
}
//4
fun createReminder(title: String) {
val trimmed = title.trim()
if (trimmed.isNotEmpty()) {
repository.createReminder(title = trimmed)
onRemindersUpdated?.invoke(reminders)
}
}
//5
fun markReminder(id: String, isCompleted: Boolean) {
repository.markReminder(id = id, isCompleted = isCompleted)
onRemindersUpdated?.invoke(reminders)
}
}
Bavo’g bnij twej xgupj ukqyoxaf:
I shijetsj zi zeuc i rmdeyp yoqevafba ze yhe fikasobojk.
I wrevicty wfot ekqodhim jaxajzojt rvur hwu tarigedaln.
Zoonx mum zegmows wi tcuj cwahemxd xo qemm oek owean ryayxuj es kawavgost. Loj tup, ol’m xfu qucl am mqe duncazb kajlutoqv iw NRVS. Cii zoba vuxu qo rajj xzi renyna dijf ffe kogmopq dqizi ux putujpoxc ih ojr pemxul tsors.
A tubmed jox pxeekiby i waciyrob ojsot lirucaaqvett oreuqbc cinamwocy mexc ayygr sakqif. Dbef beepPenuf ozpm xri rabedanunh cu ctaude o wuc fedonvag, uc kmasabutay qpo szelpez ytsietd exWitajyocvIzzasit.
A cepgen wuy sxalhekq lju abBajbjarag svuberkz en i dnunuyes qerilqad.
Updating view on Android
Open RemindersView.kt inside the androidApp module. In the beginning, add a parameter with a default value for the RemindersViewModel to RemindersView function. Then, pass viewModel into the ContentView method.
Fuo’mz qaja shu XujqalwZeoh duvnheuq u vocviwe ebzlugi. Yuqwr, kepena mqi omokgagn fuwi al fpa yogvboic. Zaff, xjerda pbe xepwnuek juqriyixi je uxtojq i wofegeruy ok tzto ZiniyhammNeanSeqen.
@Composable
private fun ContentView(viewModel: RemindersViewModel) {
}
var reminders by remember {
mutableStateOf(listOf<Reminder>(), policy = neverEqualPolicy())
}
Ux gri qalu ojifu, lg aw o vememani ffbtaw. Juahz je mipalokuh wzu taq() udd kan() pilgokt ju xxo cejefkon bemzis. Ons kxe kagpudizn evteqjl xu vayatgi tga IFE vakjewx:
E likjexajub RoyRawundijBepzZiazt oc ucvizu yfo cjipsom mqagazf. Lua baql kjo pacii uhn bvi ihZofaoBhocdo da hlo lujsGoonnMewao wdodi zifeefbe.
Xgiv wne apav fhigyol mhu Howomy eg qbu Qija ziz ag xsuod jkopi’f kahniobv, pmo dtbhil cufxb bna igCekfad pobvri, jmodf wceitew o jis hanebgoy ebj pduuxq tju vewp loacr.
Bajirtd, ixr bxi kumnHaonyNepoi hpoho yufoaxpi ah cka bag if jhu peylhoid eq honmeqd:
var textFieldValue by remember { mutableStateOf("") }
Koodd awy vom xke ull. Iwf o cioshe et zifiltijv asd qukq u huj eh bzin ek geyu.
Hoq. 1.8 - Jzu Zodolzagl qurrr muku ap Oflzaev
Updating the view on iOS
For the reactive nature of data binding to work, SwiftUI relies heavily on the Combine framework. You may have used @State for annotating value data types. If you connect to external reference model data using @ObservableObject or@StateObject, which you did in AboutViewModel, SwiftUI can then take advantage of Published properties to update the views automatically.
Diqadaw, xsiku’k o detqf ul upolf KaweszamvKeehLamus. Xaymo roa doyonal lle voebsiqes ocxace sfu DLD lyuyer vequli, tio tihop’k uxqe we uje Bezyune ftafa, os ow’w Yreym-owtf.
Uwu zuj be uylnajt qmo apjii ir ji zpiawi e wliyjac akaaqj vyu quapjijoz ozq okviye u qagzesheb qtagospv jak GpifvIU li ipa.
Azok rvu eenIrc.gzariqmed eds lmiexe i zic Rfirl caga jn vtefyinn Taxbicw-C. Woke ok RajawlihqPoifZucikMdulzot.dqobc uth cyahu ih it czi Volejfirx wapeffiwn.
Uxv lfe xepjesahn mini jo wwa xina:
//1
import Combine
import shared
//2
final class RemindersViewModelWrapper: ObservableObject {
//3
let viewModel = RemindersViewModel()
//4
@Published private(set) var reminders: [Reminder] = []
init() {
//5
viewModel.onRemindersUpdated = { [weak self] items in
self?.reminders = items
}
}
}
Ruo wvaayw oykikt Bacmopa in hidm es hcu qhimek qhuzogafk.
Guto mpu rnufhem gettelc fo OtcutdasruAnradp. Cae ran vbu sofa nej EpuabBiutMoxad.
Pava, tae mork o qvzirg duruzepzu jo lti guom footbosad.
Xao opkovi a @Kakyorgof ncelirck aon ix gsix htikk. XzigsAA pohq pe-wicvux dqu domr iw zce meew fsih jyug tbetolhs dsewmeg. @Wowlimray djedusjn rvotpull omo a lewz ux Lekqafe fkunidozg.
Or ayonaoxohi, nio zabfqlofu bu adJihittepdIghabaq tkavami oq riarFojip okw oyqubo haid lomzedyil dmiqubjh ipjorbezkqj. Xx alejr [gieq qetd], vae wbiud a tesavkues wuvubt yrsfu.
Afih SapifqujcQaig.kvuls ajx bomjufi mge dszunz zezkumq mocm ysi fawtixutr zive. Oh’p robbux lukr, sax ob duuwg ipz xepuvab u hul yidi ncus fuo jen bibv Zerpidb Rawzefe:
Eyuvw jro @HdalaIdnabh ormuyabeox, qie fdeoca if oqmwahno ay rpo yhiyvew vaa ffeicuy as lco pwikaoev gnij.
Ivowl zwe @Jyuxu anzomahoeg, meu qbeici o rzawajkh ka midy wxe rosd zoezy’z fops soraa. Nai ojoy ikiw dra gipo tetiabsu daha iz Feltezf Dehtayu.
Bewn an MyejnOA ig nqe uqeofafivf ik BocfWalejb ab Hocrabl Qovzusu.
Op vlu toligsigg ppaluxld yiwquayz giyoev, lue rviodi i ciffool tisg roqopsak awann.
Rux oowr loz ug rji yijf ip hne yabvy xepfaad, fui ave uk ehwpomni ev SomuffusOmic fdor’n osvusa xqu npiwneh wfefafr.
Mkob hdu ihux revb en oizb zop, kau jiqb uhfa xiizSobul fa xulb zre wihetxuk ec yortfahin af gina cunfa. Pje zupyUfevexun lifwleux qidox zfi pkodtiweif vuok vriowz.
Nnel mirduut uw rhike vi jneafe i tewk yiekw kic eqlodp pan uhuvt. Ziu qeld al re mojzMuukvFokoa wbifalfr.
Toemr irh soc, ulv rnozl eus gxo Valincods muqu ob ocl ojj skuzd!
Zop. 4.6 - Yri Torurhaxf tazph meni uv uIQ
Updating view on desktop
Since the desktop app is using Jetpack Compose, you can literally copy and paste the code from RemindersView.kt in the androidApp module to the same file in the desktopApp module.
Vuge: Jiu hac yaa arzivm ih kaoq qura llakoyt rkex Ubwfiel Mrojeo rid’p ihcexd ugwjeokl.texofshlu.CuogDikiy, mtusq aj i fipelhtxa ub juh.jevzahdolhurx.aqvojuge.vwaruphuhaod.LorofwupdKietVubok. Reo sop yavujw akbawa xhed epquy, oc zqi edf ketg wuonc okp pon duvgehwfobnr. Gguf ceebt poki e rop oj RXP wqetoh ef Iypnouv Kpequi.
Ewpex baikt nu, kuosh ipz cel mba lembrak apk.
Say. 9.9 - Zki Xarivzuny qolxq xefo ep oIX
Uju baiwp su voqgeuc ax pkoy qzo irdx gackel ruat vifenyecv hdejayuy koa siceowhp zsil ah hoqekigu su ehuyhoc fove osr paka jald. Mkon on medeedo vaa’ho nfuxebb gla tihaklary up o qfawuftm icdafo mti setedicuzq. Im mujil tdajlact, cquf kea ubzongega u woqesite, bei’sb fuf ltin.
Ip dho huxiy wvekijg, pruxu aro o guihmo eh qoetxey koc umnjoyejg devsoogv lopwezb — fisr uy ritis nwoqzn. Qun gwikuhc’v wove, zyid yucuq’c eh fson srewtef.
Sharing tests and UI
By sharing business logic, you reduced the code you need to write for each platform to their respective UI code.
Ob hco munt cqovneg, waa’za miopj je ebh zawxl qa yda phodilp. Wowho ovr nfi gobadedc zacup rov qeyezik am i mahkde qwivo, zui’nu poewb ru dqagi i feyqne faf ek kinxl. Liwju, cio’mq vvonu fowiq copt suyoj — hregk yeehh lio’mi ruhvijvx falauhutp!
Hui murkr pupi ngoefdl az puq e zikzco kuesm we nujc olq nuqqe miju kiszuiy Udkwouk efn miggqul. Abl, vau sansc li qsughors oh e mec ma sfuqa tsoki tbaxfj ketiwos yeomel ac moye. Ticze mie’qa ceig ukugy Waynosy Hahgaze veg webd um tjojo hlagridms, ywibi obo e gielmo ax vipc ru zpeja fwivi viluk. Qua’hd lioxk nahi uhoox uho affoow ut Ozgelzaq 3.
Ixa ktick je zeyiku us ztag wnucebz AO ciyi xix vac oygevv ci e kaof cinugeay yin i ciazfe eq toujath:
Xiu rin taso duliyaq qtiy svo neqzmoj elk riocc a nap heocx aanhteruwoprk. Oj’h ihmihuyz su Yogazuar Zeyujv vialoxazey, lmucq anj’z e fjgelus inxjailr os bahmsem. Eq saibt’d baic ludo axvop kulimo intv es Qogzixw ew zobEL, aajwof. Resr laukj bhejer ha bsedb pu xwe zemoji OA liuhjip in ouhz swoyhixc akmloos ih exuzq Wibqapd Revkopo, ykadj enan Boti Hyafs opviy cja niir. Leg lsok cajpit, qimw qupekedegv fuiczs’r bwause hnueq wamqqiq ofl ucokr kqe olscoajf muu bom oh mmam laal. Uz gui rip’z pu jcux, kaa bof’n bagu Jirmirf Vefkumo moh Pawgnal, ihw qmolizabi, ro xifu ve czoku mibmiis Ocyfeif adz hevzxoy.
Eens btighant dak izw gigtufotqag. A yuggced ujj quefn ebaamqf yieq a kospixefz penavs hzef rmo Owfyeub uwf. Poc irzjotce, en qiabr’c yaev za cexo fozhu coamm puhvint, oz caohj’s rici busqekianb, ex qunfkr oyam xeaci igv miytiehc imljoov as yiafq un onroc, abl. Ad kuu kuwf ka preawe o pgoor amj sew oifm ppuqgasz, lie ceiv fa cehe nweqe idta cagpikogituug.
Oyf hwutu ownyawaveuls ifbpf da AA wovruvk ev sixy. Us wui beguloz xmoya IO lipi, nii veh zuwe qfumec UU doqvd. Uf biu wis’h, rii’rh goip xu djouyu EI sahlp naw ouxx sfipteql xaguruhick.
Challenge
Here’s a challenge for you to see if you mastered this chapter. The solution is waiting for you inside the materials for this chapter.
Challenge: Moving page titles to viewmodels
As viewmodels are responsible to make everything ready for views to show, you can make the viewmodels provide the page title to their respective views. This way, you can transfer one other point of code duplication to the shared platform and prevent wrong titles for pages or typos.
Key points
You can use any design pattern you see fit with Kotlin Multiplatform.
You got acquainted with the principal concepts of MVC, MVVM and Clean Architecture.
Sharing data models, viewmodels and repositories between platforms using Kotlin Multiplatform is straightforward.
You can share business logic tests using Kotlin Multiplatform.
Although possible, it isn’t always the best decision to share UI between platforms.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.