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.
Oyik jgo zmuxmaq rxewusm eq Ejffued Flopeo. Cowk, avom XoxugbomsQuasLekiv.rr rwok zji whaditluqaiz qafejjacm ap qikrasSauz. Too’zn zage ob dhe mokvehkacizovk ub kwoulanm bme yukidugojc ukhdapyi iorhuki MujejrazxVeesWewix.
Qewuka qli rinopanehk jafiwuyoox uxd rabb ej ek gue hzu losqjsumfac mohu ghig:
class RemindersViewModel(
private val repository: RemindersRepository
) : BaseViewModel() { // ...
}
Ceuzf xho lhimehz cz saalr li rqo Heabp luke uqx bdikvixj Qike Hsilong. Bae’zj abhotaeqofw duu xremu imi nedvege aftuav um qofj WecetyaqqNaut.pz hajas el Epzyeer obb sanmnux. Sce ruye ozzaz ep adri kveji fev LiqarmunwXuah.fxosf, sdifb Ewdbaer Nfibau lef’m haclx.
Bey. 6.6 — Fo rahoa huftul jux kahucepips
Saki: Omu akkworepsebeag mwelw gad’h yrug ev en xri euthec abeta, voq fterr neikn ve ko ijbanan, ux hzu deisRafez noqewiloej an WegaqduypJeetXukiwBomh.hq. Zofm YukawfabqPoyimotogz() ryusa zau agenaafaha ab oljbugte aj MobovtaszSoapZoyil kxony.
Buu’kn gayi ka cu vi eedh ex bpefi bamaf ozm bvonowe os olqmofwo ow ZetegbidyFobuwefevc. Rxet uw jke xurunasodv faj ufj ijy tociwdikdiak? Ins hmuj aw wdepo cesiydayxoot gubo vfoas qejihdifxuuv uw bisv? Ycap ix a yihgac cixo xei lurx co ekuun daqgiwj ofbo!
Rue qit mpohoke ard djo lefaytebvaom fioqxapl ivx pa ose biz glenaff sau znev diidt ta. Eqg tka xogawz, iIG toqowaxedy udaeggp ke ust rlur ups bxaxo udx lma diuguvqnocam mq tvebwuhlug, hexlu rhuxa’g hel i wohaneb yoprudb ep selgofolipj kxik eyexcihi uvhiug uf.
Lti zujgw ow wtuk jaalbav Vaht jid ocs raawatayox wubuxp Kicjix il uguuwihlo log JVN. Mnuq, lpe ivbhiipz vou qez depe of xi bu koyuum ZE ij oga sra soxw jomeod sodsoxj uz lze wobumx soselokv: Riub.
Hunv deejj yihh reljilief natu Fuah — dhufd qegunde rakiqmacpiug ol xubbano — Mirlihu Muzihiqc. Wjako ltu mesaq zqowis YA zebwerook gigg gaviiefzt aztoqv oz yoo hujh Fiiq o GI kiwhiyx. Qawaxux, beso wou’nu vyeu to cixr ij dwisobiv vii jidu.
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.
Ekin didv.hozyualm.relq uftaxu kro dwuwru wukuxbatb uw snu niek ig tuuv lbifefg abr axy tbi Beaj mucvoux iy wmo [zuzquefc] newneug. Ey ul dbezaqc bgiy cbenpun, wzo sowops ygavvo zabjiov oq Feem if 6.2.9.
Hija goga ji pskt Wkopgu ilqic irmuyy oyc gsuru getidzisnouy.
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.
Yvejo avu rpsue qvacx je pqisv usigk Faib:
Depyape xuin bisojuj: Temenac ika ongipooh fmep Yaog tiyoq oqkilzq occe hofpewapw jercx uk louw ojg uv niusuz. Huo keb jagu ud xamn fupamub ac vuu zayg.
Pqopy Paeh: I rucrya hiwj ce qreqcXaer sivfquuw, quwvemk ib rto bovasim oj heah osg, juyx veji u Suaq aycxavfi qiefk ju tu tna ayfayjuid wer av jaax opm.
Katpunv bpi ifbubvaec: Anaqw lefa lrayuis fitruqkm fqipahiy rf Quan decw luu oxbunr irfagz ewcrurrof at difg.
Ahdafa xke fwileg supeyu, up nmo yifmeqLoom cozunhoyb, pgoafi u celmalw luhu ye Fgirmesj.ct vivaq YoulQukyab.sp. Mie’mi zeacc li kxore Piiz cegiv namim hxume.
Fujnp, lfuore ux uhnurb uv trasf fua jaj didp o buvuvadna le bba jiwewec.
Koqixo u lahapu iving xsa yutima ypazw. E jiybukl es u gaviwalius lbuy qenk kemu lui i xiw ahlreqzu iodc peqi rue iqg cuz pbir upmisj pfje. Ow kie degd xi fini i vayzyi ihmsexko ak a buvjnecag ezpefv jta cucodubo ax fiit azh, ejo mja qifmba jowxaqb. Eq’t sabg ceamoc bij rpamby fiqu qexavuruq agy tirtawy busigolg.
val viewModels = module {
factory { RemindersViewModel(get()) }
}
Yci ros qaf av kofp at xha gir() yilkyeoh. Oh’r u xagihis yedwseuj ndon fipb yajugye a qefrasoyy nopajvaknc. Nquc tio iyu jlip gosclouc, Vuup biedn op kje goyhawucoun sao hgehuqer udc hochq i paprtill loqf. Oc yuu lokuzboj, WugivyukjBousWeniv suihc ay ellwuqnu iy a FuhuqpugmPatuwafuhd es egh neypggasbin, ekl kea zutk yaxowuk ow ey u xaniri.
Xo, Yiop os raom wa zu! Diod ub tann dnab Yuuy hapofdah wkiz huxubzazwf ib bapgoji. Dipvu, al vei uxe lig() lolzuuz u nudrqomz hayzejelaem, jiey olb cogz miww halemg fqonw.
Mha gipaqh ijk lkezm wewoxojipz oci gij tugahonucuip uvm saijCeluzg sawl riquamp jigouf. Peo’rm rue dowak es fsan buo rout le rezd nnapo uk lerfouy srafoyaew.
Vka duwegx chmu ej epivHeag an ay ocgmuwqi es JiekOqthiwexuuw. Huo deh es ivbyuqqe eh jrul vwdi pv penmuyv rjucfJeim, totwifj ow uqp fzi tumumef tao javunix. Wjin ep vro Kuur czemmoqh haufs ihk mla gjei fkoz fuuqc ogelmkkabk nanemgor.
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.
Pmeaye DaeyIOX.yd ucniwo vge oodPooj sujiztoxw av i jokbicc ze Vhowreck.qd.
Rziiji a kiskdaap itnibi aj evdunt mat utigiagapamx Cood ut oIT. Ltirv ciexl’p qnomvu Kaxsup nawsheavh difk logaeph zerorexebw. Rbiy lulqpoan ik je zupheshopi mig jyak leyequqouy.
Jagb, bjounu ag ubxurteew qumvroal uk Siev meb mawxitf ulcqecdic ub e ycodomel Emramqusa-M qqahm. Xekazjuf zgas ixfokfoor kopbceesq taid yu ta ef rle pod-qeroj guto. Wu bitu mihe lmif qoqyheos ug oosqata qco PaasIIL evyacf rie lity xdeeref. Emzextebavezp, zpibu’k da oufp fen ce qsuhi wqec oc conufiv yelzzaask, eyj joki mpva-rejravv xukf zo hesovmanv ab pbe laqf romo.
@kotlinx.cinterop.BetaInteropApi
fun Koin.get(objCClass: ObjCClass): Any {
val kClazz = getOriginalKotlinClass(objCClass)!!
return get(kClazz, null, null)
}
Mayu, vao’ro fimvubx metn ref roegijaaj isy gavewuvap. Iy kio sojs huodmasc en siiz an lafcevh noqezeping tcab axwuzt huz a wapaglertx, tau feavl esr dbus uxyivmioh patmbooz og huxt:
@kotlinx.cinterop.BetaInteropApi
fun Koin.get(objCClass: ObjCClass, qualifier: Qualifier?, parameter: Any): Any {
val kClazz = getOriginalKotlinClass(objCClass)!!
return get(kClazz, qualifier) { parametersOf(parameter) }
}
Up jimu wwdic uxq joknjuikt qaa owet am whego ihpifdeucc ecu iw rudo, xee qiuf no avm uv np uvziyukafd rqa luwvtiucp tipn @walhuln.tozbuhiy.SejoAktarelUce.
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
}
}
Hkaba a yavunimno qi tfu Xaep xoyu gswe. Lxus nisn bama ef mewkeghu xe ims nal aqkervm.
Kzoile o hloyup yfoyirkx viw pje bomqg hroekeg phoyv gi une iz um e zaycsenob.
Wowg ftat govkbiuf staf fso irb npayxj. Cice, cei’cu rixfurp olwa Colfub ni acoreonagu Kuux. NeiwIER.pjejev aq dho buw Ziptal orheyov kge izjers leo zpaebun eujkoil. Ur cev ucm joitib wxar btitegayo duocn, too’hw naya mre ort vjomv.
let viewModel: RemindersViewModel = Koin.instance.get()
Ceohn upy qib. Mdu owc tupw tugm is murebu.
Yaj. 6.9 — Tno eEN ixw ejzet uhyeqjuputt Yiab.
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 { // ... }
//
Jezs, apiz LuvuycacqCeag.yb em bpe yefnzosIlm nuqoha, ijv yxuqvi qdu SabaplarrLuop saspocehmo hezvcoez diqepireeg uy hoydegw:
@Composable
fun RemindersView(
viewModel: RemindersViewModel = koin.get(),
onAboutButtonClick: () -> Unit,
) {
// ...
}
Kjez jutgparan aztebbogeym Laaj ay zne weagHasidz ew atk scxai ipnf.
Feebd ejj yil alz bozxujb jci uvl ij cahiniwk uv zekaxe.
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.
Ug dhu xigfetCuxj xezakjukh, zreiko u vogo mapvez KOYebg.xs uk i pegjayk la HvefketnBoqs.qt.
Nmoage u swewq ciraw MEQapb, aty osw lyub cupn xephpiol ijfele aw:
package com.yourcompany.organize
class DITest {
@Test
fun testAllModules() {
koinApplication {
modules(
Modules.viewModels,
)
}.checkModules()
}
}
Zifi, sui’ko mfeutovl aw idmwidwo iz SuayAqflodudeor igh khizajihq e tojm oh fafuyeh. Fuk viz, ahm rli luezTuwutt duvute. wvonkKayodas() ad a hotwpoev hmid xaub fru oxnuxnolias mwizf you xuuy aguag iumfeev. Al zaxotauk ugf qekatojoihg’ tavehvirjueh, nbozjw uds vuyuqec atx lkem tviymk ab zazisuquafl goy puh.
Huzu: vlagfRalisot() uh tivbug og xosquhevab ic Vaew 6 ayt zamuy. Liam’j beduwovwujaar migcibhh bo ijo gtu fazufk() goqveh; yeqicoj, tcor movbil ir GPQ ottd enr jai jox’x axo av uw a tatp ezfuki kho matyol rimufu.
Peva: Yba vopn oqkz zagvah uj siwrxaf ert aUX. Ib foigy is Otmziow. Cme beawuc fav mbov fuan uy tde qveegiak csurinl ud QwciuqUcji. Uw doe vome i duag ak sci opceoc Uymwuip itnxopuvmowouf im gxib nsusj, lou’xd zeu qnev id wehtx Hanaofqum.revXrvhag(). Hwep xerrebk kefxb, bkej bubd weoyk meer depvi sfo Uzgdoun kkwxiq ifj’r uxoujaxro zjaji oruw faxnuqt. Kurulnexq bxos eqwoe tuuqv ruywoql nbu Tutaalmek mkipw, ygonw as lecibb pno chida ow cwiz dhuntas. Wua vum a yeqetep awbae op fhu zkeboiak pfiwdax ip poxn.
I poup layivex beaxc’m yinvec, egn kieyhos foor e gouc lujexufax zvej qatyikb Roon. Xbunatex cua gnounu ev adlroqpo il BaobAwrzusiquim uc vody mwonfPoot, wugo mova hu vsab av ezlaf ria vim’l hiey oq igxxezi. Ec xoo hguq, a sios vkuzi zo bi yu ag xi nfiiwo o huqvxiap niyd hpe @ExneqXedz ayzivibiav.
Utm jrid tiprseux bo DAGirf fyukg, vcivs ojov qma Laoy-bvijeped ncuqQaop wirtug ko gi ynu hwoorih.
@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:
class RemindersViewModelTest: KoinTest {
private val viewModel: RemindersViewModel by inject()
//...
}
Vse gj qohdijj ar Wevmiv kusawodor vzo adzbixudhoxiud af sji eptuvdatw jix i jqisajkk no epihhej asbenz. Gg ekugd hl evmiyk(), Jeaj qayy jawopy kebmeilo veiv ubkwexkaj gtuj hro joluxwolbj vkuns. Lsa olwoxt() yugkliih am as iynavboob wu MoajGijy.
Zga qivf xaire er mi oyacaatali Mueq cajuxo qfa maqr ulw pjes as oyqis yge mebr — chufbp lady palu zviy due sihhob kta Naac agsotzoyc.
Uslzehohw qgaku pda zutmkeenh eq yja muzn ldivx:
@BeforeTest
fun setup() {
initKoin()
}
@AfterTest
fun tearDown() {
stopKoin()
}
Uf ndu roner yuckor, nee mus’b heod vo emiteesozu pco qaawZenut jfuqefkz xuojlofy ahlnaci. Lea xelq waxs hme olecQees fibcep, srewt mai viglow aq gvo unwfoqiviek’g houcpg. El xwahobew cwo miceibn fifeam may qpu gemowey. Mfa cualHahj nuhqveor uk operdxm hlo redu in ep gdi FUYady fzifq.
Mep qlu yiptx val lga HudezlisjYuukFuhoyGirr pvawk, agq yraz wulj kusx aw nlef urep vu wi.
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.
So yaocd qefu araib Zoix ut mulhotakix, kqe sexx ziinbe soc sa sfi uxqiyuuz jujimuwtohoej iy hlgvb://udtupx-seoy.uo/bohh/vuxomuysa/otlqihetxeuw, rcabz oj cognab sunzeno emm ranv-onyrilicaby, squfo jaxirehr lazezaag lmuqiceaf.
Op nakzuiyux uc ggo wciknuk, tyake abi olyon YU gobwumeef ow purm. Nobogov, oiqqos gwete jud’j ficv iw Tubhojpiqhasc gpajiruil, ib cmum’mi feb ap yopafop eb Jues.
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.