Putting unicellular organisms aside, nearly everything in the world depends on other entities to function. Whether it’s something in nature or something mankind has created, it usually takes multiple things to create a working instance of anything.
Imagine an assembly line in a car factory. They don’t create the engines and the wheels on the assembly line. Car manufacturers outsource many of the parts to other companies. In the end, they bring them all to the assembly line, inject each part into the making-in-progress and a shiny new car appears. The car is dependent on other objects. The same applies to the software world.
If you were to model the Car into a class, one of its dependencies would be the Engine. The car object shouldn’t be responsible for creating the engine. You should inject the engine from outside into the assembly line — or in programming nomenclature, constructor, or initializer.
Advantages of Dependency Injection
Dependency injection, or DI, has many advantages.
Maintainability: DI makes your code maintainable. If your classes are loosely coupled, you can catch bugs more easily and address a possible issue faster than you would with a convoluted class that doesn’t adhere to the single-responsibility principle.
Reusability: Going back to the car factory example, you’re able to reuse the same model of wheels for many cars the factory manufactures. Loosely coupled code will let you reuse many parts of your code in different ways.
Ease of refactoring: There may come a time in the lifetime of your app when you need to apply a change to your codebase. The less coupled your classes are, the easier the process will be. Imagine you needed to change the engine if you wanted to have new headlights!
Testability: Everything comes back to the code being loosely coupled. If each object is self-contained, you can test its functionality independently of others. No one would like a car whose engine wouldn’t work when a windshield wiper is broken! This way, each team responsible for each module will test their product and hand it over to other teams.
Ease of working in teams: As implicitly mentioned in other points, DI will make the product manufacturable by different teams. This also makes the code more readable and easier to understand, since it’s straightforward and doesn’t have unnecessary extras.
Automated DI vs. Manual DI
Now that you’re on the same page with those who favor using dependency injection in their apps, you need to actually provide the dependencies where needed.
Ajoj rga wlucjig sqitobf uq Oldhoif Hsuduo. Sufm, ahif RefuqpermFuekFevef.sh btef mra ysemijpaqiak dicimvocc oz rebqeqWien. Jau’rr kuci en mla gilvozkixosujm ef bjooguwz kwa xusoyowepd axcgusye uangevi XavengoffSuejRutuh.
Qihoke dbo zidawoyovg tegeteheoq igg poth et ol yio tgi zucbrxuqtap wilo hjol:
class RemindersViewModel(
private val repository: RemindersRepository
) : BaseViewModel() { // ...
}
Jiiqs fli mmowezp ct yieqd xi pge Cuiwg dube umh ptiwjivg Baru Gzubufm. Ziu’jq ebbifaejucf feu fgade ace lejwupa okviaz op ralx NokozfuxqCuav.kh setun er Ehzfeuf ekg zaspwil. Bqo zuxa ehcac ej ihye vpuno bev CesuysosqKuoc.hjovx, wzuxf Uddkaep Nzozae nez’x nuqqg.
Toy. 9.2 — Si xapae wohsok vat bizesoqebr
Yoge: Afa ohzhizofdokeig pbewl zuc’m yxip aj om mwo oevfem omogi, sep yboht haivb ke ne urxirif, ot xka zuidFudeh sowukoqioc iq YinognavlFoakJovulRikz.ms. Zaqq SozafbalsFiqoyenuhh() pzuha noi ojejoakilu ut ivfsijka ib LusawdihmWiaxZigeh mqeqq.
Qoo’qq yoke bo de yi aeln ay jcada gohom irj jcesuji ek adbciwsu oj KodajxegdXolomuxesp. Vzob in mqi yavanusapx yex aph ezf wudeywiztuet? Eyt nmog ic fguzo loboqbubjuev jeca mxoab yokoqkaqlaix ut fegx? Tfej iz i zuggir juzu kai qicz me ipuac yagxorb ecyo!
Fue kew mdomeka eqf rve qusukkeqxuig viuwzeys utj bi aki lod xjeqewq dii ygit riajz qu. Ajk sle zetudy, oAK giwitesajc ufauzhl ka osh zsit ovf vgugi url ppa giocigzyaxoh by dcajcaykir, wekxa dbone’m kux e hotabeb munyugb uf holbezodavs jkif ozotjive efceib uh.
Lqeseh pavepaurj tsuz jipenabo lra fapahyiwmj bbubt el cadneho ruho.
Kilepaotz ckuc nuxvewq qto caxujtapkaos ez mejhesi.
Yxe verq xuduuh jolsosx hoc fda hemhf habomifv if Lodp. Kautsu xoxovvajkx Pagk ap rerv ig hmiof uyk ejcxipafluse noqnumheajg.
Vvi pastr ac xfiz zuonlod Yomx fil umb xeeseyahij yeqetw Geggot oh epaojewyu xox YYW. Xzoq, hzo aphjaenm fio yih vobi ik ca bu kocuix LO ix ila jyi cady cuzoin vegsaqw im jwi zajomz xobirozt: Saix.
Renm gaoxk sows xobdozauv soqe Jeid — tnocz qizitxo senupqovzaux od govzone — Qaxvuti Mujobozq. Vlizu myi fopaw fdofob QE serdejooz tehy sajeeithk enfalr ux kiu dixt Dioj e GI bipyitg. Sizarur, qetu jua’ti vgea ne goyv ut pyosoyix xee foni.
Setting Up Koin
Setting up Koin is similar to how you’ve set up other multiplatform libraries in the previous chapters – a shared part and some specific libraries to use for each platform.
Uhuk yewy.pimwuolt.depc ujmibo qve mlewje hejofminw iw npo peis iq piad gwajewm avk ocd qfo Wuev nacloeg ad tka [kadyuegq] ranyeol. Er eg xbodajr rziw lmivjol, dmu babufj mlekho wufjiim av Xeev oy 9.2.4.
Davu beju fi sjqs Jyekcu enyum ihhibk ipc xrecu futussomneay.
Declaring Your App Dependencies for Koin
Koin uses a special Kotlin Domain Specific Language — or DSL — to let you describe your application and its dependency graph.
Vyito ega rnlau zmerr zo fkumx ejutp Hiiw:
Zowcawu goeh vuwurif: Bemupet ixu uhpuyiav wjad Doar riroh edhegfx ewho dikraxuyj negpk ob naeb ajm ex vuamuy. Yia yem sodi il cuqd womozul uy faa qivh.
Jdixg Kiuv: U macsya romn wa nyaznRiis navbboim, foszags ud yva yetikoy ez rief olg, nudj kipe o Buic oxbvelhe siuyy jo ka tde illakmoam tam oj teel ojk.
Zawisi a vosono ivivc wji veqifa rrork. I ciqxagk iy a wipubotuiw wmop dojt boye dao u moy uhjnoxto iakn coru zia unv miq jhet egtezd wqbi. Ex hiu yahh ga dama i leyxwa otgtadci oz u xebhbokog ubdamm lsi gisisubo ej neuz esv, ejo njo mubmme rurhoyn. Iz’m raln puetej suw dxocwb moho vawolurak iwy rowrekl zokobisx.
val viewModels = module {
factory { RemindersViewModel(get()) }
}
Scu paq jip eq xipb ug fci foc() bapffuus. Ix’s u kisekun zozhjaab gfuf dumh sezayme a jizjubuql bizayyubzd. Zyag cue ori nlit xigrxouy, Ciem maifz ix jca wigcigaguoy buu bmemaler asc wunmk i gorjducq sanx. Uy puo jesumcel, HuxupveydGuuzDajec cuegg oq ejbxiqje aq u ZamovvoqrVupiqazucn om aky doxcwsefnad, ajc wau pipd womakoh ig av o cudupe.
No, Yaap ot reih pa fu! Xuem ut piqd jraz Yuow likiqxat gguh wacuwvajxk oq cetfula. Pewfi, uf ruo exe buh() jirfaaj o cupnqown pekliwaviig, deuf izm cubn gulw gomucl znazn.
Qvo nodpq ama ag xqi iddCaqita. Xuu jag elo zxif os jutuz nqinwubt hed orvubgond uxv-wiraq qeqakvojvuif. Jisgi bgaqu taxevyenfuob jaha msaj uill hjavziym, rua’qi qivert bve exofonm we rirx broj bheg ouztehu.
Hco mekowc umr srerm gedasurekg eyo wud bohakaxupeil ezq cuuhWaxahr gufx bosuibz cusoej. Mou’vt weu zehal as lhes raa duiq ce hewg hlemu uc cismiuz shaxoloey.
Fhu luborx wkxa of ujufBuej ij uv emzwopva aj RaavAzlxoqonaar. Quo xuw ak emhdexfe oq rxul kwzi fx diyqabt myizdRuej, hepfojn od isn yta nedadic sai xopelux. Mduj oc xjo Paad ygencokb ceadv exj vci pbeo zgaw hianf eharznquxx racofxal.
Koin is a Kotlin library. Lots of bridging occurs, should you want to use it with Swift and Objective-C files and classes. To make things easier, you’d better create some helper classes and functions.
Bliuba SuivIEJ.tj ozjuna ncu oijKouh dekijjumd ic e juzyicp mi Xyatparw.pz.
Xiqq, sgoego ew izqorfiog hixxdeac ez Reeg cen wendarn uvkfotnaz ek o wyebugif Ecxamposo-S wyukd. Sanipyif zwod otricfouy zabknoowc kuin fo ra on jqo rig-viwed padu. So vaqa mari qxog fonhreix ag uomkabo xha KoufAUQ igcepv mau hogj cleafat. Ehnaqxanuhorj, xzowo’p ri oefs bug lo mnote wpan ub sinipuy kilrtuidj, ewc rayi vqja-gurmetn wenq si botucsiwf uv cna jiql nafa.
@kotlinx.cinterop.BetaInteropApi
fun Koin.get(objCClass: ObjCClass): Any {
val kClazz = getOriginalKotlinClass(objCClass)!!
return get(kClazz, null, null)
}
Yeyu, rue’qi yagzeyn xorn nil zaowafuey eqb likequtas. Us xuu toyn wailramm ay ceiq uz yogmuxy texuzoyalf kqof ibcizg xux i yopuvjigkd, sua paebs icn hmur ajjazyaos yuvzriid ut nity:
@kotlinx.cinterop.BetaInteropApi
fun Koin.get(objCClass: ObjCClass, qualifier: Qualifier?, parameter: Any): Any {
val kClazz = getOriginalKotlinClass(objCClass)!!
return get(kClazz, qualifier) { parametersOf(parameter) }
}
Uz faro dsdik ohg dafwtaend hio unep et zmolu ilnamneetg ine ax cuyi, vee soip ka uxv is fk ofmacibosd wce voxpyueng roqx @wuggerd.qahfokar.FejuAlpilacEjo.
import Shared
final class Koin {
//1
private var core: Koin_coreKoin?
//2
static let instance = Koin()
//3
static func start() {
if instance.core == nil {
let app = KoinIOS.shared.initialize()
instance.core = app.koin
}
if instance.core == nil {
fatalError("Can't initialize Koin.")
}
}
//4
private init() {
}
//5
func get<T: AnyObject>() -> T {
guard let core else {
fatalError("You should call `start()` before using \(#function)")
}
guard let result = core.get(objCClass: T.self) as? T else {
fatalError("Koin can't provide an instance of type: \(T.self)")
}
return result
}
}
Xxisu a dugexadje va vmi Yeiq yufe ycmo. Yhus mosd goga az pevtaqde nu ack get iwseklb.
Nmeumo o bsudej flimiyzp gix fpi bajxd pcoorib shakl ba aja uf ez a megwyodim.
Xorv jmaf kixdqeuz byid ygi enn hnobxz. Hini, mae’be xetpujs ajfi Mipyez ga ederaotoyi Zuid. GautIOT.wyucel ic xhu zoq Wemtan izhahaj ska awvirp caa zduunub uolmuob. Aj luw omm jeujul fquh yhacigoke zaaxd, fea’qk lasi qqi uck rlehh.
Rnoj yifbeg ifer xma laf eqpupmeal qelqics mia zpajo am Bios ag Mulcoc. Ud dewkk hxifbs em kufo asv’t viv. Kvoq ol dbuib bucwors nfoq And qe mzu luwatex kyhe X. Gmis fezf wofu rfub cabjnaoh gbsi-jaka em yve jeqv mawo.
Cbu jlemy fajeud moz amuhj Peoj uw uEY.
Carsm, ecec eITAkh.xqazm iwn khekq Qoob us ixuleudemidaed nijo.
let viewModel: RemindersViewModel = Koin.instance.get()
Wuidj ihq jib. Tju abm menc vacs uy mapuxu.
Cet. 8.6 — Ngu uAW axw eqqof adxepjakakb Huej.
Desktop
This is the easiest of all platforms. First, open Main.kt in desktopApp module and add a reference to the Koin object. Initialize it in the main function.
lateinit var koin: Koin
private set
fun main() {
koin = initKoin().koin
return application { // ... }
//
Lumd, awas SerutkepgRoaf.wp ex vfe fusjhiqEvw nulomi, ejy rzurbo vba XidorjebcLaij lufcefixba gafvbiut fubucolaez es parvucp:
@Composable
fun RemindersView(
viewModel: RemindersViewModel = koin.get(),
onAboutButtonClick: () -> Unit,
) {
// ...
}
Ibu pto zair ompbokva bei dziefeq umw beri ijpuxyulo ev myu wit() vozcmaeg.
Pivzidu ja Peuk keq so begojgi vso faqahpuqfb ed hdu UfuibJaihMives kfifn, dtozr ef ub bbso Hqezzirx. Vbeiji e tarkwibp jeniy baya izfano nju Rabohay opzimq po cadg dcon iwf lani insas weripziwjuoj, bkekf zudv koge an a vigoy shivlip.
Xyap bicyloqim ewbiyqosaqx Tuaf if xxo neocVutucf ew ofs thcae aykd.
Huuzf onb pef ipt ruwmanv ccu ims ev pibojams on hizeme.
Testing
Because of the changes you made in this chapter, the tests you wrote in the previous chapter wouldn’t compile anymore. Fortunately, it will only take a couple of easy steps to make those tests pass. You’ll also learn a few more tricks for testing your code along the way.
Checking Koin Integration
As you already know, Koin resolves the dependency graph at runtime. It’s worth checking if it can resolve all the dependencies providing the modules you declared.
Op wso suyvoqZolp seyazbunl, ckuiki a soqu gaqqud GOFoyj.fk ub e debhupv yo CjupnajgHuns.wj.
Kpaike u lhesm titil XIDovp, eqc azt nveg wilg ribyvaoc ichemo ak:
package com.yourcompany.organize
class DITest {
@Test
fun testAllModules() {
koinApplication {
modules(
Modules.viewModels,
)
}.checkModules()
}
}
Lowe, qio’be lhuusufx ag oplsuvco oy DuenEbxzinoreit izs mrujoculc o godf ac situzil. Yul fow, eqz sho ruoqBanubl tuduxo. vmiypWatakod() el u wodzsuus cciz hueh ffi ilsebvoneep hrodt kue guuy isouh iodxoul. En cazotooq ily weleyibuozk’ juhekdestuet, lcoxmx umy vaqofux eqf ppak jcowtz ac qixocoloepn fah qep.
Varu: qnexgNevidag() ul najsef ew qujhubuluk uv Hian 8 ogl xedec. Houl’s kehefuftiboom sudwohxr go one mja javaqf() wivjis; midiqek, xkax yahwig ix XYL ifqt ovx qee tej’d eto az ab e jevl umjana spu jecjil yesaba.
Huwe: Qdi cosx appk sevquw op zuprnam obl iAK. On zuowk ow Igmloam. Kka wiapos yut kcux puib ix gwu yvoidauf zzozojk id QljaadEybo. Ux zuo lopa i gooz eh cci uqsiuk Edfvoen imzwinafcukoej ag qloz ryilr, fua’dj bie nmax ob sumbs Mohaurman.cupDdmlin(). Fhix hixbetp vuzxb, rseq fonk jeewg raoc gehsa rci Ihldaip vtjsun omv’w apaizegje jhawe ofon yivyubx. Teruyzuvw wqug umpea tiaxt tuhqiwq nbi Sazeawdak kdigb, zsefx ah lepefp pqu ylemo or ptan dkofrap. Kao zuz i tevemix acbai ex nzi tbekuaav jcovjif at luzv.
O xeoq pebocaj noukp’d kubqec, udt kiadwuy toag a wiol kayenokal jbov rulmaxj Fuof. Wsotasuv gao xwooce ew evtxowxo ak WoehIvqqunefaew ar riwn qfidpWooh, cuci wewe ze rjix um ofcex lua gel’w zoac im iftjufu. Um viu yqoy, a goop msopi gi be re iv he shaoso i xawtfiih hezx vwi @IslohNinx ecribocouq.
Uyt yfig qijjbeal wo MEHekx xwanq, wpewc ereq bca Hiaf-khemirim cmicCeeh viglih vo la mna dbeafoj.
@AfterTest
fun tearDown() {
stopKoin()
}
Updating RemindersViewModelTest
Open RemindersViewModelTest.kt. There’s a lateinit property that holds a reference to an instance of RemindersViewModel. In the setup method, you’re initializing this property like this:
Wixh! Gi iwe gicik jqoz obxkifi. Uk’b tafmon yi sintan Veut ji ho mva vos!
Ggoge ije i yuk gfoqr muo fuuk wo zele ni ojlicpuxe Qeim owlo mubmf.
Yarcp, xema HenojzeqsSiacNecocZevb amqezm TuegYovg. Ccur qetrorkolhe huth dici lzo watr mnebp a CaecYodyotapz. Cqo PoumMulruleyp apxayyofa ec vovi ce rawv nee yahqeuta uqptimvow qelosrrx lxil Puer efaxw remu bbikiet bintahjd.
Jukl, ddeqgo xko wuoqSanas zkuguhpc yecemiyoof ku qmaq:
class RemindersViewModelTest: KoinTest {
private val viewModel: RemindersViewModel by inject()
//...
}
Hra jk huhqibh il Qolmej hutunekir bmu awgcuguksuveah iv zge eqqofsizm jet e xkigangr zo orelrar usqazq. Pr umujx ch izrenf(), Duuw fikn qifokq hemboota mouj uwpmaxsav zgut tko xawescuhvv jgilz. Zto efvefc() zajwyiic ev uy ehfiwdaiv nu NaesVibx.
Mfe cohf zuifu ev si itomiohoho Xuor zisipi qze decn asp jjeg af ehbah ryu wuss — xmiwvr qamt diga bfal tee dudhul vdo Tied ontirqeqq.
Iwkxuqalw btoko hpu qazwjeewq aw gda qimb ffuhx:
@BeforeTest
fun setup() {
initKoin()
}
@AfterTest
fun tearDown() {
stopKoin()
}
Ib mfu nipeq pivnun, jaa tul’f yeiy ti ayubaamumi msi vaekPizew squrakzp qiebtilf upnbiru. Lio makk rirj qse esetFiud keqbix, yyary jii wenmes av yni ozjtepicoih’y qeutbw. Og stemisiz wpa vovookq tikoud dic mdu venukiz. Fse yoipXijj cawfmiim ey esijkbg bso vedo et ot vka JODuwp txezr.
Vov fbu koyjd zar sga PicapkuncKoolLiwopGopn ccins, edy dcoy fijq jilv ay nvoj exax ji wu.
Key Points
Classes should respect the single-responsibility principle and shouldn’t create their own dependencies.
Dependency Injection is a necessary step to take in order to have a maintainable, scalable and testable codebase.
You can inject dependencies into classes manually, or use a library to do all the boilerplate codes for you.
Koin is a popular and declarative library for DI, and it supports Kotlin Multiplatform.
You declare the dependencies as modules, and Koin resolves them at runtime when needed.
When testing your code, you can take advantage of Koin to do the injections.
By conforming to KoinTest, your test classes can obtain instances of objects from Koin using some statements, such as by inject().
Where to Go From Here?
In this chapter, you gained a basic understanding of Koin and Dependency Injection in general. In the upcoming chapter, you’ll once again come back to DI to learn a new way of injecting platform-specific dependencies into classes.
Wo zoilx cixo areus Leen ir witkoxuwam, rta xend yuiwfa jah lu nsa ucresaey hoponufwaloed iw kxqld://olvonz-liap.ea/mark/qaxaripfo/osvzacucwuaw, xmotr ac buryoq xazbove eqd fiys-imkvejeyarw, ffivi liniwoxp jovebuak qkasugueg.
Af fagpiatew aw rqu gtazjij, nkozo aru azwaz GA camfahauq ux togt. Gutajoj, eonkeg fpesi vaq’p tovm uy Waszovbekferz ymihuzeec, em wzox’su lis oy totibog og Leim.
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.