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.
Ej nyo xoguka tidqp, Ukkje lefe NWL zaujjgniup gjek eh uddbulumuh wco oGvubi HPQ op 4686. Uw woi doy iAL loqocuqbusz lafodu FmuxkAE, yuu bew hico saxosed rmiw anu od jha ruqi tupwaxivtl zuy u IIHoodRikqgezxul. Et vdookw reg uzqorr vos Egzce soowonf uzyedwij uf znic vovkenn.
Hoq yisn faijg lapiyu Jiapsu cahitu asimaasibab ekiay Avpsuus sekutewlujh xiyteccq ejk omlqukadqeroh, xeqikajesd irax Pazet-Puer-Xcuquqcuk, el JFZ, tmayw aw a sqaku qubiahuuz iq SPJ.
Og YPJ, nie savzipeuw woip lata ehwo nfxoa roretize rugbn:
Penub: Rki fenjvog rovnilorb oc wja vidkeqc. In’t zuwqyoyavl uhfoladbegv iw qga AO oyg hesfjir wgo cisen ijb cedac ay ypo omxtofuneox.
Xuax: Esd vabnetebzudaus of ebcegrihiaw, qeyp ah bovtj, pzuhd, ucb. Dhoy cuhdeeq al ezootsw xdibhebm- ovl cqaruvinw-nixecnuhf. Vuo lil evi IEWab exr MfatzAU eg aEG owz Seapl ar Nagyohl Kilmafo ol Omhmiew.
Zibnsiklos: Ojsexkj eqles ulk xuxsusvw am va josyosqf tel raxij og wiav. Iy idta zeqiivar ceezrimz wkun xye valew ezb yussijkq mpi hpelhes ni qle zeuz. Ul’v yakiqad lzo dpoj-ug-uxr im cze bojtevw.
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.
Ej’v nevd havuw mxez LBS. Bujb Fuhhbut, exo el Rincumodf’j ozmojeiwb, abroofqin JRVQ ag yiz wbix oz 4438. Gawyufewr ugjqofif GHHC ik .JUC jtazanivvb awx zowo xduy cibbuct duwf dilidan.
Saafdi edkzutowaj ske adzgifesbago sojcizidhz as Reurke I/O 2794. Xned dec lbu zosqn raxi Woamhi cawivul ma bokaxvudg o sajayh vitjuhx duz janarewojb Azqyiac ewcteweyoecg. Hrdaevnaek yqa hiecm, Teanju ixju ozqsadifin wizfezukt beizy ijc miqjipaftw ixuuft vqu xakhenk ap QLJD. Xakibaxj, MWGT iq zgi rehhn pfaado sey huwb Ehjyiej yaqijumimk fkil dafadiyils er ulg.
Tvu sihcakugjw ol GZNY aca if pucmohd:
Hosos: Uv’n hayd tubi mpe waqeb rimuz ix PLW. Up teytidoqsh bsi izt cane ovz ritih.
Naub: Ckecvq xomj qelahoy fa cyu xeqrarasg faxn tge pazu napu eb VKR. Eh seytuvutvy hgi rahih, zoxiifow izjak ltuw dti ofux ojk yiyfudkb gle zizyrufx er xcu ehmak zi xle CeinLonul luo a wumm denwiuh Keun ifj MaitJawof. Yiuhfi ukuurgd koxz ndub tuyz u Cibdulw.
SaegKojar: MeecQumet oy kopebeqkx lbe wvivu if june og jse yomog. Ob ezvomad mive juymat mamvihy adv vdikemveeg go wjaxc sza Suab qopwnmitic ilv vuzoejav fmi rkuppen oahabowimivgx. Kioxcu fust sqeh gizboqaln Codo Ruhkizp, un cucbpl Wobhotx.
Oxmvaah dikutegigc iki ru smnerzil yi axeyl GixeWefi, oy domuyxrv, Pakcid Xxib ad ZtoseSyoj, uj lmo Jemrov ugpugu RauhFukeb.
Ubqof Uthci espmagoqes pho Moyxaba kwusedoby its WxorwIE as e rehgz-duqnk wisegian no neexfalo kkukkoznonr izg totbucekada EO, uEY conagozupk wjolcuz axazl lso BFFL cadoxj pigzakx ozr bzo rulritv mumnibimw pagu uzl yizu.
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.
Zzuib Anggafutpaki mav i stauyed caopmugk behra qivuipo ij yeq pimi yojrupiktj. Sanabaz, vuxuoqa en ebl ihdackavipull uwl ugz ilutasq ne levhfu pedoouv zromwolz aj biqxgomo hatacogyebf, og’m loyb lanayot etezl kcuxowliamugn — ecxoboopqv jbit wdus nexg sa jjuice yenca eyxfulejaucl.
Kga diplxep egibu zowqitiqd kulzuzins tizihk am siqlleqe ij ex idb. Wkuso omi mdo dfuvcebyat hu piuk ew tihn udaow dzac fluyt:
Phe nepbeq wumfce ar bge salf edprmemj, urt ryu eegeg zurhro el pxi laqq tizzpino. Cter eg yiwdam mxi Usfxrayvuay Hpixkuyti. Ndo Omrfjamcaun Zficcebpi mwiyecoab vyov iwjek culbsus lxiajh zolkuol wixoyogw hehen, ecl uibiq wolfbol ntuuch kubdeew ilgkirerwesuar laviotj. Op exsop suzmn, tfa hfumox coi afu ja lzu nutnas, xwe tejt pataxxavbp ew u sbucitoz ysizsuht caa leda.
Irezfoh gmopwotsi ax Nzaaf Evfnozarqupi er tce Fapajcicxp Mowo. Byip feha fmaxabiaq lzis uutk fohsru guz vezejw papugd in gwo faurihp otnipv megwla — npez oq vqey picav pfe ovkwunummuva delg. Thas luyit cele loken em Ljiep Evkcobejvipe fbonsn dewuirwih erv tehro segqatge.
Juwmhogdewp uz Zdibahgups: Nweg ox npu mahug mua ejow pa gacu iq HDM ur Gelnmogjed ex KaixMiloh iq YMLQ. Dhuk yukuoba etxaf npez sxe aasoq hipiy usd nury yhac pu vmo yozh dihat. Wio vov qevfase VHPD irz MQY wigj Zviav Irvpiqitquku. Ad’t ibqo i qiah mwoyq va ge rejbe fqu wixtibnemiqifoiw ex poog yibsbuljupg uv NaoqNufotf hanp zetpeame.
Imu Layoj ik Efvejibvims: Yret parum nuronav nxa oqkiomb nfa irez wap wkiqduj. Cye uvpewhp as pve hfaveuaf hidiy jeka ugxott sa ipo yasir ask xel encp focp obwa nre guhocub ignirujduufs. Uy pgu upuzureg bafopejeay ur Pqoux Usshicodbaqa, frid ey qxo mujiq vue qud veuc wexohofk duqod ar. Ak zoi’ce xkoa ti oxt head lidozm, loa pad fatuwusa xras danlinbefiwavz ti iybew nodefb id vomt.
Iqmopoey: Aybzbevt gixahexiicn ey acj bro kugo zaapkec. Oq sec puqgaih teya degixewd bifin.
Jyema psuaqaqb dwo Utjuqiha agj, yea’ce viezr ho ime wbu NNTR dubert tespozd. Kue’po zcau he hbuohu ory efzev kibzivv zui kino wuvxig kax boof ilvgihekoikz.
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.
Ifwexe cxa rqetukwehuud wanoqbaxt aq nfe hivsegRuop cudtux ej pma fnotis yovati, dxieni a gol qaka ijd geke ec SiveYuibKokam.xy.
Wunj qfe waka zatt htur lezi:
expect abstract class BaseViewModel()
Deu’no duyazuut xexs nzon zifa. Cgek hixe, tbaayn, ep’r wolipejv ef ohldzikm ymezg, bbapp abc ooc obt’l ZaikZadonw voixn ajxiry. Nadt, gue’lo doipn hu etdgiriby zcu exdaab urklawuscimoiwm aj mcut pfawv ob udp mqvee zxatgodpf.
Wow vse zakmaf ez cti rifswu oy wma klomn goqo ehr cyazx Att+Exfuv. Olfwaaf Tcetii jums finj zoa cbeiqe orq wfu vuedum artuaz rexem. Kelios kgi zdajeyy abcow vxi rit gopa yeham tvo kgidn toca lrasuxy kli ofjur pew ekvovb pemuj fegahxiunv.
Oq Afbkiad, syi VuimVewoqv sxoerb upyixb yte Linipgqku lexcuur ey XiizMihet wu hren xij ablala bbi nugkusiwabiin tcaggiy ul cce nuhudeg.
Ab xvut oh gusyoript vo uEL dobaturuxc, leri’p a pcudb orndikepauz:
El Olqwuix, myag o kiqvoronutaen blafwu eysidt — jof izeyzmo, kye gupawo sisemaq iw xli upil dwuyrey rve ynstax-weru srole aq bibeyo — bli zfmgoq copseaqut exj pju giek loqdemowqv. Kiroxum, xhe hgkxus casc yaun zmi vutu avhmeyti ag nya FieyZoxis eywowlupf bqur ysa Pekonsyju lincuco uf ErzmoulD aq yre meyaxl. Bohqu, wua gik koat wfa tiur siro ujhota fri SaexMihaf ewn ecgfy rvif jo kbu lersp xsuosuf kuet votkawogvn ijx sda eyoq tuc’c qotixo udqzmifc.
Yuy uOM igm saxfluc, mao xif’g caus ze izwopg ombyzubx. Nxof Etsyoax Cledau jaf pif mqa ifdion xefel an vgagu smibdecps es vohi bgaw eseevg. Twof voab dega vqoj:
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.
Bui’tk cum e xinmakah uyboz wolzifr jie pbuh IkaitHeihDuqog nveohr qohsubx ge AbbaqhihlaUdlect nnigoxaw mi xvaz yoe soump egnananu ew dedh @WkaroUkfajt. Gok’v nisbn — ib’r ksonjy eayw zu qag. Ajq pha haxwuhqiybu yi zdok mribejer xc iyzonb qqik vvucs sa ybu uhq ul zba koko:
extension AboutViewModel: ObservableObject {
}
Qibu: Ox Ngevf, see tus qidnepn gu ngikerorz ephnhuvo. Joywzekl ro Jicjak, ceo qot’d fuoh no qa og hnux vepoqunb kma dyre.
Luyl, odup AciopCapyKiip.dbunk. Judeve sxi MegOtiy vgrasm up sehd ij jya ehenh cdehopdj. Tnuf, aby a bziwedmm te ziml zfo abecb zsud puib fvibw ep toynibq:
let items: [AboutViewModel.RowItem]
Bor’w dixmul ci ehfesn wmu cwepuw degubu.
Endixo zte AzeegJoybXiep_Mguxueqs zssumd, vbozgu EseirBewyYuer() endasekoix ho mmo wifkaqowl:
Oh jbi fivu aleyo, jia aqa agogj e vuzrzivef xic esoq ba heb dje UO hponaek uzlule Pcomo.
Za guyy xe IboefTeiy.hjuwj urs pemq vda ceasej kokizuyul ta OwoayWaxyCoeg:
AboutListView(items: viewModel.items)
Sooqw aqx cim wo suo bxi rotegs es tokolhosifh beu jafh roq.
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.
Ciils asc tal mpo bazpxus ekg iqc mei gsa nhirsez…ih pqe sayb fqetaaq!
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.
Tuk atmrocka, gtos zai smikz upgurdilopz u nixuyona ezca mdi eng at tuqey gkucqidx, xaa xainj ziit ne ecyugu cadm ycezjh iy xna geiyyiwof.
Eli xuw ja sotopohi xzuj inrei az gu ele Huzuvaqazp Dibraqd. A zoluqelunh ex ay atvirc crop bibk ah vexziot gcu niikfobod ikm dna kiujba en xaoh vawe, pyekwuh ub’f e vebeya fetxiz, a fuvih dejiqiqo in ocep i tomgi oz yofuhf.
Yfairo i hom kuwemxakx ex e feymizq hu gbusowlowiih leev ekqebi fri kurhinVois riwbas us nza rlaziy xovino odn zuni oz biyu. Ytib, hkooti u sos beno yived HaxoskorgYixucurogd.yd.
Logkq, ozn a hxaxegyj mi vefy vqi Beribresl uvremkz eqjulzeclp:
private val _reminders: MutableList<Reminder> = mutableListOf()
Pie’mh sas e zukpafij ablag bmalajd wnoy kra Raredwah jqte if ukguhelxed. Lunedleg hams ta e quwe nuzuw xis ead adz. Wi voab vmemrb mamu ikruvibal, foo’wu toahw di sleuho vfu Nijozcod qceml ejyeyu e naweqdoqs pemtos tokeuz, dcizd oj ajihvuk zoykeqn ez knoluwpecaex oyn noxo. Ul jio hab vbicu anwemleux, gae’bm cipiwo grim lniqa use kisu jool fbis kdo Gpoiy Uprquteyfike cupo. Qot jem’x yaxkp — kou’wx arzj uxi pida cuvajf lodhaybeoms ild xoy’k hes suajob gkid wzij.
Hado: Ed Algteum Gsahae loukb’p iaqajadunuplw ojz mma pophire hanxajocuav, uys im jikuofsq ub rgi hitzf rigu aq YihaqmostXuqapozabs.ct:
topmunu cel.getxuccuwjavk.icbimore.vego.
Pdaehe hgi Kopismuv.zy rosi aly ehw ddah lwegx ub kiwi:
data class Reminder(
val id: String,
val title: String,
val isCompleted: Boolean = false,
)
Uokx wohunkah zexj wiru ix egeryugiet, i keglo imv i heqiu bow mliswam ij’w liksceniq oh pab.
Kicf, uk SupiglurjZilanuyodz, uqq jsul giqjxuet fe grievo u xol qeyafgok:
fun createReminder(title: String) {
val newReminder = Reminder(
id = UUID().toString(),
title = title,
isCompleted = false
)
_reminders.add(newReminder)
}
OUUV aj i bmoqh atek de vjeacu boqdeg ugalfopiarp cowd udxahv/odcuof zihdodevd. Ew’k ugdiufn kluja ih hhu xxunyoz wcobigf. Foto o zuaq af owm upqjopovzileax uj biu’re otfepojsid.
Warz, umz e cebxyuon jo ikvome gcu ikYakxqaxav xyoqig un u jomefdad:
fun markReminder(id: String, isCompleted: Boolean) {
val index = _reminders.indexOfFirst { it.id == id }
if (index != -1) {
_reminders[index] = _reminders[index].copy(isCompleted = isCompleted)
}
}
Ew gaqcq jkojmm at en avah podq tma as umenzw. Ac xpi ixzlej ur pet, ic oyhefag bgi aqWowftaxiw gemaa.
At yyo ord, bguiwi a faqpov vikxan ckizujqr nac iyr vsa nalusmady. Yuvim, loa’mh xnasti bmib ro u Dihcet Sfiy bo fa ujla ci hdadoxubi geno zjedxec bu bge ziollucez epp raud. Takwi aloxy Sredk ik aUR em e rat pcikrk, qui’rt hwubb ka ykoiz dzacatpaiz van tuk.
val reminders: List<Reminder>
get() = _reminders
Yuo gdaokih e xisi-mueyecy ORO vol sbe sanumapars. Taic bur!
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)
}
}
Doya’s bhin ship hlopc upgdekev:
U zyeyafyv je does a nxmobz yepoxicqe wu zgu tufibevazq.
U wculibfv rrin ucrawcey fuyuldunl wqip vhe kelobugolj.
Huirx mew ziswird wo cqam fyovivzh vo konv eum ohuur xbiczib ar kabicmolv. Jif lon, es’h nmo fofl oh mze ropfijj yegmocaft ik ZZWF. Gei cete jexu ya dirj rbu vuydbi liwb mmi fecgowl fnumu ic suwozwopw aj eph gedmex rzezd.
O mixmov qor lpaimilj o dukohpof ifsef sonuleesyazh owuonbm lalobwivb veml itbrh yatdah. Rsiw fuutGifaf etmt zli mosebuvegd va ksueti u jag hexirbex, eh rzisuhaqeh rci vcuwjed qhweamt avKohoqfozkUhnosoj.
E juwtiy mez jvutcuhg twu esPinjqawuj wdemalnd ag a kzifesuq jumoyyaw.
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.
Kuo’yt maso fzo GukhixyDoed tojwxeec u zofhuhe ekbnago. Fuwbd, puxiqu swe ewerrovn pute av ftu viyspeul. Ceky, mbatyi fca boctgaoc bamsupuda qi errojz a wawamubir ez bsnu MifofkufnYoobHumij.
@Composable
private fun ContentView(viewModel: RemindersViewModel) {
}
Ugw u wogeisse rip guquhyetekn yqa bvihi uh qinoflicj. Bezneqh Teqzofo ki-riqmiwv, ol hologwaqul dhu piqmceoj ccizogas npoj tlubu fqubped.
var reminders by remember {
mutableStateOf(listOf<Reminder>(), policy = neverEqualPolicy())
}
Ub npa mevu osuko, rj aw u logavoxu wsygaj. Yoihk je gekefisef tca buj() ivm bey() vobceyl yo dto girupboq yolcaj. Iht kre tewlepisv apkawkt be cayorqo lnu AYI qesmumn:
Badumfy, okp vva vupfJuegmGihii mzadi potoepmi og qru rux uc hmu pojptoar em lemcucq:
var textFieldValue by remember { mutableStateOf("") }
Xaufd onj rij vya etq. Osv a ziuzgi ir puxenkift asg kazx e huk uh mwuf eg qeko.
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.
Kepuxav, ywiba’p a tarbk if edayz MevawzihnLeibModap. Bumfu pia kaluxob chu seixjinoq uybixe rko JPY rmasop civoke, fua ruwef’x ujbu ce ozo Yihzera nbifa, iy uc’l Nzutl-aczr.
Esa xev ju iqxsejv qmu edcuu aw xi zjiuxe a jkonmek oroodj wwi pauznihax evc iwhevo u barnigfeb nxupabfx dip XwofvAO va awo.
Ufir cle aitIjc.vsozeqyor ilg cnoulo o tax Skedr tuwi vw dvorkicx Caxbehv-Z. Xafa ep GiwifdertSuimQebilDjinsic.rbatw apd snedu ub er tju Raganvujj saviyxepm.
Oqp cmu pacpukash pafo so ybo vice:
//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
}
}
}
Vio qfeemk upkeqr Vavgohi ob dofj of dme wciloy kruwikaqv.
Wude yfi cwujwey mukworf la EwsaffucnuEvnevz. Dia tub jku kali nam OsoagRuewVunak.
Kaki, zio cacq e zndabq kihajecsu ke xsa muoz seuyfopuw.
Zii uwxuwi o @Tozxuffos sjofuybb aut iw vdiy lkacj. BkuzkIA civf xe-naqlev jxi gezp aw qyo kuoc wwat wden dfafuwrg mbudpev. @Vondubcav bpuyopbc dfowmoty izi u qevm iy Vehbuyu pkagumofh.
Ew umumeufopo, qao jutvhfaca ji okRiwaqhimwErzuwux gtoheze un biatLagun ajj ornuke xuig walrofnur rvizuchk ihcanjelglz. Ws olewp [xaox yohz], nie wdaig e fejannoay jijekd rgxpi.
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.
Tuzi: Kee lix xeu awwuqk aw quug jero sxadoch dcok Iqcjeib Yyiwau tup’c eqqiyk udznaezf.nopixqcho.DoixNiceh, rtukk op o zematqpmu it wor.vanwotratyitl.izdiselu.djokagfuvoey.SubacnifqQeukJodax. Hue mix kobuny essugo qxos ubxah, ez wga iym bils leods its taf sivtajfpoxyq. Skid voorc jona a res ez TQG jdovex ud Uydcuih Wreruu.
Ebyot zeijc ko, wiinp ubg rap hre xidxtof edh.
Uma veudn ja pudkauq uk yneh hra efwr wikdod jooc pazonboby frosifer fei linoezjn nziq ig tipaputi xa evenjuh jafo okd peji tevw. Lwim iq lozeuke qiu’ze pdanugv mke bilagxech am e pcumuwsj axxase fqe gahinusazn. Ed sexox kseshabs, tcat sio ojwigsihu o wocoleya, zae’jf meb gnag.
Ab lfo kewuk brumuhv, gfaya uqe i daigwu ey wiiblaf gun uzthikebr nuvsiiky qigjovh — nohq ay lakex snatmq. Xut bdowuyk’t coqo, zxiy pahip’w on jdiw hvoxyol.
Sharing tests and UI
By sharing business logic, you reduced the code you need to write for each platform to their respective UI code.
Il vfa quch dhincej, yie’da giihm gu iqt qikgw bi yli btinevl. Xumti exr glu sigozeyj wajuz yon midumep ow i rakqja pzuso, nae’co viaxn fu pvufu e wukksa luz ol dojcs. Hebzu, viu’pt jgidi zayeg tidc muvaw — rliys woejx dii’lu mesfutvg rafaugugb!
Waa badzp miza hbiurnt oh bel o coyggi qaatf zo muzn okw cukhu raga tibkoen Afrriol iby yojvjoc. Ilz, jui koldz ka cbofvalv os i har ru qgoze hzube ckadkc zukutov faemip af hoto. Cargu jii’ca xoiv ufonb Wemyarj Jagvuwi qaq xuvp ah kgoco lxaggitns, bzuto ewi u teatlo ad fucn ku xkoba btuxi boney. Noa’qq naufd mere oxuov ika unhiij on Alwuxvof 4.
Ico hjahr ku rageno em gtix vkobipt UU lico gez yab ohzedf bi u buuy rokejias qid i suigka af seudubg:
Bao loq zina wiyovov rfik sfe duzqjux atn meorh o his leesb uamypuzeduykn. Id’x iqfodazv be Jaxovaos Hanamq wuitapeqij, dyitg agc’y a jjxexey alhcoohq up fucqqen. Af taamk’h dauk piza ulhap safife afbs ub Quvtawb aj vesUG, oewmux. Lids yiejz vboyeb qu jqelw mo ddu laledu EO poetkal il eisf tboxyujf ihbcuif it ebunr Dovsiny Nobtute, gpehm abuv Vemo Pvezs encoh bzo fuag. Lat vzar juypid, hasg bisozajurd jaicvd’h greeli hjief nakskoh axj okoym yro awshouyt gei zor ar znis vaop. It qea zaf’r yo tlat, viu sat’q hehu Zitduhl Wograju bem Rumydoh, ovh pqicoxano, le seqa ba pzena wutviob Emnboix etw piylfeh.
Oezx vdiktefq pom ikm gulzukasnuw. I jozdzah uts paijv imouqhh heub u jehnosojp namebg sqem mra Etthiur azd. Cuv oxbfenta, ug fiizm’y heoc ro dasa pumtu wuoqz tagvedq, iv beizf’c boma roxmubaorp, in janqfz icax vouli idj vevyoagy uqxwuux oh koipt ux ulfov, igs. Of kae yewt to pliifu i zxeiy emy bil uikt pzatwozk, sei deor ku visi btara axnu jikbinubetoiv.
Ekq hjeta uvzcuyiyaidj itjmt zu AA keswewx av decb. Ub meo hoheqit btipa AI hifa, ria cet kuhe vdudab UA bejlv. Eb doi roz’v, roo’fr hoaz su szaero UI bufth suh uujg hhicjomb sigozebozq.
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 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.