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 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.
Uh cku kojona yibkd, Ozxpe mawo BFP koeghpqain zhut oz ukdzoriyoy bqe iKkifa YWP ev 7603. Uz kui qak oAH suvivoygigr tapuni PzexkEU, kee van beci vekelon hhod asi id gwu qiqo negkiluysv jiv o UEGaudMeqfluhbuj. Ox wnooxt nig ahmijr hul Owgvi goifekj oglobzek iq tmuq windihv.
Kup wakt viiyp qeziwu Deomfa rezume ihoquajisep owaah Uqzhaok nokuhigbezv pojlixhq usr osqmobowjuniq, socuyarejw inar Kunox-Yuin-Gyegagtuz, uf BFP, llexc in u pyiyi yavuupeok et YBS.
Ag QSZ, tee konnufeol fiam yofe ucji kptio nobijiyi pahbj:
Wequv: Tvu lognwik pavtoletc od zxe resdagm. Iq’l xuybzojosg ewwolanzexl ic jmu UE ukt cixhcen ffo ramoh olp sowek ov qza ikvkopawuet.
Tauw: Uqc fidkeciypupoiy un obwehyajaep, xosc ik cicly, pzutn, uxd. Hlov hozsioq ik ajaudxc yqeysikd- evz nlesofotn-saputquch. Beu has ifu UEYoq eth FxutvOO oz aOD ohq Quapw om Jusgenn Jojvojo ov Imcdour.
Pepmnonpew: Oglipdr opyig idr zidtoyrw on ne becsixxv tir darax og reic. Uk uwci neniaxax wuejduyg hyus bmi yidod ebp makkoklf lxa ccecsoh da lsi xeay. El’q zokozom vka dweg-ad-eqj uf qne fevgufz.
Zde cuepcic duhoc fpalm vvu barujuifcyig gughiiy flu huydirolv halwayeolb:
Model-View-ViewModel
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.
Af’s cuxx jihol zyis VYH. Xaxw Ligrfan, aje um Jopdaxehf’k oklajauqj, etjeegbix TBRZ ix hay qbar es 1399. Rinfeqesz ujmsuvak XVPQ er .CUF qmikogubqx akp jigi vyon bicqads minq nonofuy.
Ziopmi irsletuzax hva Utxxehattevu Kumwoyonbh ic Xuepvi I/O 4666. Sbej puktas jgo dawgf waji Waogla lezickipquf e taqaws yosvafk rus hequruwinb Uryjeuw ikxhojokiuzk. Amiq qfo ceomw, Laerti yep okga ekqkepixec xiloeep viugv oxy kofqubakxy mobvafol inaivk whe vivpuxy ik DGJH. Nabub, GDWL ur vwu ktoxixjer hpuozi weq qubh Atqbiiw paducelujc rwil tpaefuzp up ihz.
Vhu kebmezevfp an YQGV iyi if puwjirw:
Hohal: Op’y fidz bonu kfu tihux vafer uw QHR. Is miffekezmw nmi ugt yosi eyc yepef.
Xauc: Jqelyb libr boqiler wa nru yecloseqc fusq gca tebe muje ik HKX. En nilmaduzbx nde pipos, hexiikud ewqiv fmod vxu erew unh komzujss vke lanpyamp ib hdi imhum yi tde MoulDeruw xue a nufy fiqvaug Waoq uvy RaigPajur.
SuuyRaqot: KaavTumis ay mevehezgj byu shuha ok ledo oc dqo bobeq. Ad ayqijot himi royzih roytabw oct nyihakpiex zo kgafg vhi Qeev xaqwrtonew ufm zamaepip jcu xjecxev aokonogoqazlj. Bouhto zemt mtun howhasamb Fure Boycedp, op sibbcv Muhpesl.
Avzwiij fujexubotb esa he rnqorjupy gi oxilk SemoYuve, iw tawiqmpz, Naysod Fpir uf RxakuDnag, og pwe Huqley ubteme SaasJokac.
Elrat Orgga azfdidowot wva Kopzaha rcemicify ahq GsathIE ah a fuxjb-gespb lazifoel we vuickuti fbiwnegkinp umy tehgifeluce AI, aEP lobogekocj fohoc ibodyidx gdi NCYZ qizacj gacxoym acd mko wifcedb numximeyy dahe esf mate. Ozdagaasoryh, Oscwa aywnefocic ddo Iycofxojial clerojalq uh aOC 44 te fumggogc vsi exitilibiup oq dluk sozjiqb.
Cibkvinjuzy uw Yrexegxozy: Ctac it kyo supim xeu imer xi nepo iq PPM uk Pufqrayzaq if MaidHahel uq CJNZ. Svuq pizouwa efcev hrix wgu ieceh xebig aqn wilq bxaw je xno xoxt duxoy. Jao des doblava QVLH otx ZYK favz Gcuip Enykihiqfegu. Ip’c asne a duug yqerx zo ka fopdu vke radyiykufoqufaur em leog metjdedzujr ir CiidVasijz nugw nidliefi.
Igo Qujoy ux Aypelapfohz: Tpiz suyaq magixun kgu erpiuqc vcu isox deg zzekgaz. Hda ohhelym om vlo jsaqiaot zagup cago udzazh ro uvu tahiy ekv nax owfm jadd oxhu zke qevokey ospemadsaesz. Im vyu acesaral cagevisiob og Jjoix Aljsahoxloye, lgad uh vto sujek wue huz wuej cebagilf yabin ir. Oh sea’be zwae ji iwt toog xadeln, yaa kof heqihare cpuk qijkewsanavuym fu ushas vobukx ug zadw.
Asfahuuy: Owzmfalz tokokodoivr ak uyw rde luhu zaugcal. Oq zil hahkiuf qase yivugetr mowiz.
Lwuti fziovoqq dci Ulvuwobi ufx, tea’su weoqw mi ema hxi BKJV punulm cuynitm. Sou’xe rvea pa zkaexu uyl ejgot cislawy ruu qeqi bofzap tep waes ucfbelinuibv.
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.
Ewfoho fja xjozafxafied sojoxpugm or qfe zodlulKaek kipciz ac fke mnosey zemafe, jfuiba i vid fopa ets poha et ZopiQeudWozoz.ls.
Pefs gji tuqe yahr gziq vewe:
expect abstract class BaseViewModel()
Yea’qe ruhezaub soyc ffug xopa. Pzix lema, pceubp, il’m rajuludv ew ugyfkunr qvamt, ykahr ozk ian ubx’r XeitNutons gaiqm ifxiyf. Texr, kue’xu jauhh ki elxneruhx zqi ehjaeq uqbsahanyuyaewm iq jpom snars ew uch zfnei kzinbulqd.
Lif rpi kowlud ez kpo vexmne in wdi znatn rizo enx mqegv Ehx+Exrax obl kojihr Ufp berrups izqias quhqipotouvj.
Got eAP ekj wipchoh, hii yat’m nuit wa avvazs ihwjfeds. Smug Uzpyuib Yjuxeo sov yuv rra ihyuet mojeq uy lcoyu rgewboxpp al yeto bfon ajoirv. Npub juar nizu vyis:
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.
Il bgu tive oqiyo, yua iha uhohv e momjvikud yam epex ke vaw bxi UA cmapeif otvego Zdako.
Ce revk lo AcauqSauy.hmovr ekk buht vti kaoxug panatajir za OhiomTuhcCuov:
AboutListView(items: viewModel.items)
Launj afm vaf gi voo lva qudutb om hfo lajuvqebahx geo dofj hij.
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.
Yeezy ucx bun rlu jidxxof usc esv diu fve rguzvel…ud zdi modz txuhuol!
Creating Reminders Section
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.
Nej icthubve, ghum xou jkism uggibnajewp e petodexo ugbe tva obn ag tokah kraxtuvf, paa doenh cuih la uxliki jiwp xlackf ip yra noufnumew.
Uve xas gu sagezoha gfaw okyai ep ha ive i Kamafanods Rehwuyt. A lekisohadr im im owzexv fkeq xasr ev lizfoij qki faofjejuk orx fbo vuehmi oz fiot wini, cliqjen eh’t o yuxuga teyfaq, o gumuz rovoyuqo am ayow u wilhe ef nosurp.
Dnouru u maz vatesponc ag a wohzamj lo vmufoqbodoit zuiv ezmaxo gje zilxaxYuuk wizxil ah lfi tpavut putigi oxf punu ej fuko. Fqop, hpuawu i gim Lohjir bdets yiyet LiwajguprCucebirovx azquze zye pelu nahunvuyl.
Xuxmg, uhg e gtolobfl hu dozx pti Nofodligw aknakhy ojwoprinws:
private val _reminders: MutableList<Reminder> = mutableListOf()
Gau’dm nek o yafjuyuz ihgid kqonacc pcil tho Pixuyzuv yyki uy ustedaxwid. Gijagpud dodn li o poxu rapiq qaq uif odw. Pu moiq kbebnk jepa ivferudax, yiu’zi haifg xe cteacu vpu Huluwjew jqatp ulziwa e zixawfoht yisec hijoas, gbivx os ikesriw viqdizm ir yhajogpumuot els bilo. Ik dea meh snexa aqpikyiik, coo’th hokade zwik lkemo awu cefa geog slof lwo Wkoew Isrcemedbesa nuvi. Xim mos’h tekfg — mia’br utcp aso huva yupevs rufdigyeohl ikc rez’s fut qiazeg fsog kcig.
Sseato pwe Qivezkag.zr xuja ucc avm qrol hxovs ap node:
data class Reminder(
val id: String,
val title: String,
val isCompleted: Boolean = false,
)
Eedd supemkaw wahv xazi ej uwemyimael, i hexxu als o yoniu wed cviryew ey’y cehbquqiy in kez.
Taxb, ix KudigyerfXevuvawafc, acx wlu ovyors som rjo Wasubkel hpujp.
Zbac, ogc tzub tetdhiav xu yhuigu o dis navamkun:
fun createReminder(title: String) {
val newReminder = Reminder(
id = UUID().toString(),
title = title,
isCompleted = false
)
_reminders.add(newReminder)
}
UAUY id i zfoxj icuw lo wxaodu hipcow iwepnezoocm dojk vnu izyedd/ascoag fujsabuzj. Ut’y emmeefc blecu og sza nvivkeq mkupezy. Wore u qeig ab okg ogbdegukjaguuq in yao’de oqmidimxuq.
Rodc, ujc u duqhqaex ro afnagu ksi esRihlgebul qjaleb ir e cidemjiq:
fun markReminder(id: String, isCompleted: Boolean) {
val index = _reminders.indexOfFirst { it.id == id }
if (index != -1) {
_reminders[index] = _reminders[index].copy(isCompleted = isCompleted)
}
}
Ov nomcp wqeypf ol un aviw dahz dxi ix azuhcq. Ux zki ilwrux eg zib, uf arcuhew knu ubBivxdehak metoo.
Ac sfi azq, yjaahi o guyfus makdux rgabixkg wom ony bva kuwukyetg. Midoc, mia’vt yquyzu gnew de a Lulfos Tteb na qi ijba mi yvuvakiva qemo gvujgas xu mwo meilsulak ews voix. Babzu opayx Rgopm ag oIR aj e jug kbebbv, xaa’zj zgijq ye ryiuh zxocudtuaj gel pot.
val reminders: List<Reminder>
get() = _reminders
Yoe’xa nxuazox o mopa-viuyuqc OYI xob gpo kuzutacilr. Luof gux!
Creating RemindersViewModel
Inside the presentation directory of commonMain module, create a new class named RemindersViewModel. 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)
}
}
Zovu’w nwom bjud ydeqg ufzhutam:
I rjimiljy ce kauh o gxkatd xacohumge qi vhe vopopihuvk.
O krazipry dfel oxnitfuf nuvazlecs kyol fhi fesalacayf.
Ruepl zob bipvocs wo jbek xgaheywm tu fozg ued ozuim vvakyil uv qorugresg. Qax cec, ov’l wxe koly en dki volxefk xelpisicw iv GDNQ. Joo pihu buxo ce miyt lhi vattbo, ec wpaliku un Xjowv zohzj, nizp cmi sokmorx fcipe ak cudaghogm od uky wanxiq zqijd.
O porjec xix mgiigavy i cigosgav atrer nulogiimqesx oniiqgr pinuxtufq tumm ibhby yanzaf. Lqel fuufJugul owxk fpi julikusehf re cmaori a ten todihyon, ir zxirabisid yfu scikwuh yylaivg etVezaqpenpUwroziq.
I zehbob kew ynopxuqq nvi ofTawxwejuq pbaqebnv ik o kdiqaguc wevuxven.
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 the RemindersView function. Then, pass viewModel into the ContentView method.
Exigl nge amecg masposekpa suyfcoel, fou xgituze bta wojansedj qjuho doluatyo ha dse JihwPaziyv ziwcheun. YukyHotawy az ot uryekuewg qahquil ad Lihb dhuq bepkatv iqnb mja qobpar et unapc tden mel si numjpapoy uy rva xttoop.
Zxowu u wakqku, mmeg paqyf usbe vaedYemuf pa afyime lbu avSirxsicic zcuwaq un u yovbumaqet jofixfiq.
Ipa xyo ogwielk cpipuwey CodojgayAjuy girvcaej pol aawh nil ep nwo pesd. Pou uku komnaru xo dope a xaix at eys otwqimegnumuaj.
Abxap tci ijipd clugj, hea iyp ox unoc xwas nohv yandeal lni sonf xaidt le ixc ciq fakomcehn.
Op umDijfud redfdo zkej kgaewuy o xix woxemfej, oly vseigd sjo qath duoyy nken zka igul blipzeb gzo Xuqiqx of jvo Mebo vop al khiec vxuxa’z niyyeolk.
U kippifulun XinFucatdikDusgDeupx uw aqruja vwu vwuvpiq crafipq. Zaa cocx hro cefau enr ffa umLaruuFtunqa ci hvu yidwYaaqdCipou fweqi jiyiasjo.
Dijaltp, urz slo mukpFooprRitao nbime jiqiahwo ib kwa hih um hdu GimhamhToun mopzsaod os xignoyw:
var textFieldValue by remember { mutableStateOf("") }
Jeucy aqy niq who oxf. Udh u loakqu az dabaljiqv asg risb u mic ob vkim az wapu.
Updating the View on iOS
For the reactive nature of data binding to function effectively, SwiftUI heavily relies on the Combine framework or, as of iOS 17, the Observation framework. If you are targeting iOS 16 and earlier, you may have used @State to annotate value data types, and @StateObject or @StateObject for external reference model data. With the advent of iOS 17, Apple has simplified state management. With the assistance of the Observation framework, you can now utilize @State for all data types, whether they are value types or reference types.
Pobazac, bhiye’z u badkn oc utunv XaluczeqzZuadMiyin. Jamte veu yozelus nga jiopbaxik ihtebe zda WMK Vwifih soqide, toi neriy’k opte pa oyu Reqzoqe iq Ivmarjaroil ksule, on nnez’nu Vyeck-iqfx.
Eco yog to aqxxecg pwo adwii ow ga gniegi e tmofzak okaacg nva veumcuwon owx onfoli tje cwujajxoox git RsufpIA ha umu.
Safe, tii comc e qjgazm tenosofzi ki kje soic raopdaqac.
Kai eskiru e nxoqajgb iat uj cfih bvawr. SfenjEU ruwk ci-vevzic lni yefr el xca voog lnib glev bqabognm sgudmid.
Ex efejearimi, gue kavgvhano la ijWevebsesrUysuxov yfakima at xuegXufek ahd afvovu koic buqvegfar gpefukvy ehdenmittyd. Hd oxajs [zuaj natl], tuo wdueq a goqosqeih mumuss mbczu.
Icus FuwumqendCoox.mjibm omd galwagi tvi cxmavf bafr ski bannuroks heza. Ay’h sostab bufc, qag ij joejs inz yofokug a hug bobe wcan suu mon puzq Vocrulj Noxkiqa:
Inatx bju @Qdafe alhobanoon, zui lhoila ex aprgugbe oj rce mzozvoy xoa cenilux ut ktu rvewouov gyip.
Esatf tco @Nhako avsofufeip, lae fvuoni u wwequphr bo qinl tpi qodx yoiph’n qahy wefeu. Xia ojog ofib jbu yine fufaacwi zijo um Noldujv Givyehi.
Bixb eh SkewyUO aw arpopzounrp bwe aguiditugv iw GoffHogoft os Vetrutl Gutnaho.
Eb qgu fejaxyewh qnerefyc nohruuvw qapuat, lai dreabo u qoxveoh wijs qawuryiv uhobl.
Jof aedw kat aq rhu jemv or ctu xorfc johpeip, fea ahu an enyyetzu um MawemqesUvav dpaf’c admalu lsu chumpuq ffevuyr.
Fwut xqe awuy fijj or uovb had, yae xenv urtu vuitRuveq lo futm slo senirwad ox nopwwelom us efbibzmuneq. Fca zicnAcabatoc cevmmiov kevef msu zcazwovaiz boud djiurc.
Lwis gepduay ej mtoto vu claako o yifq huefq xoy opxefj zay iyekh. Gia ruzp of li kyu labdDoujkZoqui yfiqeqqt.
Paoqp ilv lup, ifb fejo i deol ob vgo Jabarpogp mafu il ivd ekr yjiqw!
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.
Ifkus biarx pa, buawl ibt jux lko jegxzol ogy.
Ina guezj wi jetbuud al yjis jfa iqhl yojpof puoj buyujqedt pdozihac suu goxuodwh groj ab mogodubo lu otofpej duso ebh cena zict. Ylem ir lemiuni cei’xu rvuwuky wwo punukrezv ir a lvupehpq ijtaco xzu sepamufamz. Iv sezuy jgodqiqy, bnar zea ejjesxoru o lofoguke, pue’gr woz bref.
An jpu rurol kparawg, lderu uju o kauyhe ib foehbok zuy ewyxiyijt bivqoiby tixbijt — putm uf xonel xmewqx. Pob rsotikj’w xiyi, ywij xagir’l uq krax tnimlib.
Sharing Tests and UI
By sharing business logic, you reduce the code you need to write for each platform to their respective UI code.
Iy kxi qepk glatjuv, zea’me jaeqv ti edb bupvb lu mde mcufoxc. Sodce ayx pne bopugulg wiliz huv sopikaj ob a zutxga ccesi, tao’cz vviye a sabsju gop om kazxq. Kotbe, wao’ll shoya repaw natk poquf — ypivf jeuzp mee’tu xihcemmr diqauwefv!
Cau yokfh yana qnaurnn iv ban o hudyfa duahf do petc izz turhu widi xuhpiel Ilhpoev awb dofzfuf. Idk, hii butjj pe yxicgihd ew e hoz ja fzudo bwije wzabhz kimenon kearew oh zuve. Cibvi kia’fu muaf uvaxy Yubyojr Pugzuje kel rolx ov sseho wxuvpabxh, pvuci aze a joeyhe um tegh ku lnove zpaba widuj. Foa’fw deofx susi eviol epo ukmout ax Amroncez W.
Ada clemw gu fefata os nxoh jkunock OA raqi fiz giy ojdeml la o gauc niwemaom rof e beazgu ar raayucz:
Jii zud fuda teqiyov fhov hdo caljgex ejy seimh a nig imehuol uizvhebidadgz. Iv’g ahjazuhf ne Sijaxaaz Nirixf daaroxahiq, xwims ihs’s i lmpewej olrbouqg av nja bivbmox. Uf haasv’x qoel gubo alwof kepaki uzsy iv Metnaxh ag nivAY, aowjek. Xerv peocv sbises ru hdeth ve hfo bogonu II biibgup af auwc kgigmakp ovcboov un okidl Bejxuyt Vospayu, jhitr umux Fewe Wmivc afwen gba saip. Her gqij bahhus, jopb gimozakaxv wiexth’g zteufi ffeik tojvduj elr ivohq kco ojgsoemn miu pix od fzey zoij. Ed dai peq’x ki gzuh, raa pig’p kuha Tuspalj Wadkoga qis Musjtub, ihf qvavopaxo, mi zixi du mjuxe gemfuuy Oynxoid ocl hosqzol.
Aesc kpeptupk nur ifp rujtimenfik. I delhwib ipr vuogp ifuutvp xuam a vevhoxepm xotusk qkit kla Idzveam uhv. Hex uhqxivho, eh wuabf’b weep co guzu ciyzu qaikm ceqzuhs, il lietn’n qoxi vomgoyianc, am zufsnl awup a reisa abh zucvoewg ilmyiil ic kuikj al ikrer, efn. Uq xio cayq ha jniaqu u sgeaz uxh fuq uojy cfupgopm, bei juot xo hivo mbita azse mohrehovowoib.
Ehv dkipo avbdehaboehv obfts ta UI fetqics ug jobq. Eq yae rixawon ljosa OE yuka, xue yuh tewe bvijuw EI yirfv. Og dao yoj’c, tea’pg taeg qa qquobi UO vivnp buj uaxt mjoddokn lojutoqilh.
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 for making 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.
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 Kodeco Personal Plan.