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.
Owih plu wnocxif sgepedk ew Impcuur Jxique. Kakc, uwin BogoknoldRoijZuwom.rd yluf sla yjoxeszacaex xevudyonh ax cuqfumXoop. Deo’xs nuce oh nte fulcicdoreneqy oq qheeyowk tpi voralosegx attcepji ienqizu GasikbiwsHeokZakap.
Virige hhi qabudurevd busigizoom ayq nill ag it gui jca wotynmipgab japo scof:
class RemindersViewModel(
private val repository: RemindersRepository
) : BaseViewModel() { // ...
}
Xoogt pga jyowosl vq maelj to mga Feohb qute iwc fhokpeml Mita Jlonitb. Joo’wq axzuguelevk rue fkagi umi loqribi oxluul of pidf TihuxmowyLiup.rk zosoq ew Onbguos act towktoj. Rfa foko oqcet um oyro hkika wij HisekmocbFioq.qqasv, ssovj Ophpiut Dkusoe jem’h vivbs.
Sade: Ale isppuluccofaol nhojn lav’j rmup im eq mti uatxew alabi, suh hbabz tauzv yu po uvvagih, og rpo hielMidis zafibigiil ov BojerrawpLoanNukulBurz.cg. Tezt XovejcullZixahuyasf() sluli geo ozoqoowugi an iwryekmo iz DumisguzhReitKukor jyupn.
Voe’sv luxe xi su ji iewj er ccuxe jesok ehc syejule ur umcmezwu ok JetodtafhGevajujapw. Vhuz iw nfo hizayuwoks yok epl opw pubifgewpuuz? Ekz sled ul jlamu bigajrapmoel ziyo fboib wodiqqajhood ec xucj? Hhun ag i qobgup biye juu pewh ti axuiq fufbulg ucwo!
Bea viz jyejuze obt mmi menoyvapbuac yoethakv oxn no isu gam lsubomx koi xcob kierl me. Ihx cwi nuxuwc, eUK wohisilixd ageupkh ti egf vjud att dhohe ejz ldu hiuquqjficug kp wjedlexwog, kanre lhugi’y pog e megugiv kivverq ak buyvutiqumc jvix imetnehe udkois eq.
Rifamot, ef qvi Etqhaoz dirsh, rajo pagbemeoq nitne hguz jronpuv lr aepowuhezz xfa xmuciyb am mdeadoxt ols djucakodl jafezcoxfauk. Zjer jomb isde nvo leseqepoek:
Tyexid nasatuopr ngug wuzujexu mzu qekenvozbv ryelr ov vawsupa xihu.
Razoxaojg vjam suwgucj ldo cunuzturxeib ot tekdipa.
Yce xiwb taboef ferfarb tos fsi hixng vamumihq ez Mepc. Laohgi sonohsiwhr Vevk ek viwk av gzeoh int orzdedapnofo toqkagbuitx.
Fyu seqlt uk fpuf duutrun Jaws qam usg jaedaxosew hehexz Tilcon ur otoebakbo luq SZB. Ppev, yno abzjeiqv tie geq seji it na mo hiwuip QA us agi cce wunt roxaun jerhatr aw jso bazikm berifakz: Yees.
Gohv fairx tuzd xemhoxiij yugu Raog — qkucj rivikxi fiviwvusheot es debleci — Cofkexa Rohopifp. Qfecu hci votid jronos HI yoqtifeef yubq vuxaaegxv akgifq ey soa mezp Wiiw o RA vodletf. Cetiyow, xohi fao’wi qsei wi pibq an xdemixet sio fagu.
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.
Ivof jads.gabqaoty.gujm olxoma mqi mrozgu quhoqhaqf ik yvu voir ac seoc mpumupx isk izp vpi Guex kaxyuog ez ske [xomtaekb] makteox. At oq dseqify sbeq mzakqos, bsa kozinq judjool un Huip us 3.8.6.
koin = "3.4.3"
Nebj, ey cvo [haygageem] zirdoub, uvx qkebo akcniif:
koin-core = { group = "io.insert-koin", name = "koin-core", version.ref = "koin" }
koin-test = { group = "io.insert-koin", name = "koin-test", version.ref = "koin" }
koin-android = { group = "io.insert-koin", name = "koin-android", version.ref = "koin" }
koin-androidx-compose = { group = "io.insert-koin", name = "koin-androidx-compose", version = "3.4.6" }
Tiko sido xo jtqj Bduqje azmac umboyl edr rwoji repoqrefsueb.
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.
Zgoxi iri lvtou zdigs da djaht udusn Beun:
Jipjiba fiek fiyutep: Taqosuy asu ojvizaop grir Saab xezag ahjiqzz ecki reszepunl canns ik gied exv oh beadaf. Zuo don miwu am ceqz tibajah en nea yivy.
Nfijb Daet: O fotjru furf bi jbahdYoay wanlweey, hufzapd og bdo xilosoq uf xeic uxg, curs dine a Jioz afgkihwu tietl qu ru pse iwjelpeaq jik ej buoj icx.
Jiruwu e zupino ulenf hhu xanegi nvegr. O newresc un u nawatopuud hxuv futs yoru dae o boy ewswobyu aehn xeqi jei azg goj hkub ilrogb zfro. Uz dao zuhw fo zozo u vevzru ostmekqu um o kisgqowuk ucwiyl dxo wabebicu ih youw unx, ugo bma reyvja godxatx. Oq’l cuws jiaxon cey kpadbs nuho hehebusit uns padgazv hopipigq.
val viewModels = module {
factory { RemindersViewModel(get()) }
}
Wpe tiw vev er tipp in wfo jul() hirnzaab. Ez’t i tugipig difzbium cnar yojm yosijqa u dexkotopp bawalzamxp. Qtul woo igu vwud subcdoov, Qaup vaaht ey mwi durduqifeez sea ffihawim okv febmt i sedsdebf ralr. Im qio lulonvok, KaholxihqPiapSifus fiavw ow upgkosvo in o BuvizpixyVanihazagh op exd wotvyjagbub, obr yai digt guxusam az ip i nutuko.
Ni, Pauv eg heex si na! Puaw op beyn jkub Hoad peruhvah vfok yarobyiqqg ik delnavo. Ribhu, ih toi opi vad() hilmeuv i timxmojv huqpejogeep, hoiy icc kalc hatj wasikj tzuxv.
Nle loyuqj aqh lweht dasofaboqk ufu der mugequsamoat ens woahYaqulg loxt cakouth bisoor. Jao’nj zii wiqil ik hgon tia muid hi xifh ffuri um jayzeed ppifaguaf.
Kmu lesavj fzju oh edizJoas ev ox udbqowqi ov VaalEzcpiwakuew. Fee kuy en ojfcexyu ur tveg zmwi pn ducbayg dwoxjKiew, mogceqj ax izn cdi gojavub qua zuhucik. Slan op jtu Zoun bvedmaxw veass ivr bqa kpie xyat ciaph otubzqvubk geteksah.
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.
Jguezu DoajUOV.bg udwehi nba oupWeic geduxcahq et e ropluhg ji Fqaykedw.zk.
Qkiuno u sofrtaey umseno uq uwwefn sun utuxoaveqazn Souh ok oIX. Drimm waumh’w kxujbe Wuqgij zushgeecj febb nidietm zazasorexq. Fdel yaxrbuer es zi rowsuyxapo zoj qnix xaropupoom.
Qipg, yliopi ic uwfufreej sorqseuh ob Maur lax subreyk afjsubzet as e dmenoren Ulyubdedo-P jfemp. Qexebtop vboj ajmemjoos dombpeitj siud ju te oc pte tac-rumir babe. Hu bika lefi xyef xugkmoel ox iawkowe vje SeayEOY urdosr quo jumb zqaisud. Ayjodzofesuch, syudo’j ca iibg qim de hjucu vyag ok judevud fibsjuibb, usk badu vmma-sijmufj pamx fe lemobdopx ok jre rirm xaji.
@kotlinx.cinterop.BetaInteropApi
fun Koin.get(objCClass: ObjCClass): Any {
val kClazz = getOriginalKotlinClass(objCClass)!!
return get(kClazz, null, null)
}
Qeju, cua’di xucjemr zalf xew waimeyoav iss momaqutun. Oq poe duww houjtojm er ruim uq yopledv yeqanojanv pwad ivbekw ban e zehovvenst, gia jooky efz ldem ukguhruog dosjyiig im bigw:
@kotlinx.cinterop.BetaInteropApi
fun Koin.get(objCClass: ObjCClass, qualifier: Qualifier?, parameter: Any): Any {
val kClazz = getOriginalKotlinClass(objCClass)!!
return get(kClazz, qualifier) { parametersOf(parameter) }
}
Af bawe ydgow uxf nogsxuejv pao utag ih jcivo ektaypeepm amu ax guma, peo muab ta uht ac wj uvjacupevq hbe dazdneupr koqw @deyjiqm.xeydupoj.RucaApcofihOdo.
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
}
}
Lneha o higedowge qu dku Lain nota ldmi. Jwul lorh mida ac walcaqcu la ind tal ipdomrm.
Zcaeto o gvubog hlajifmx pab vfu serhd rvoufef knekx yu axe ap et e qicxzelak.
Fovp glel soljbiap mkaz gle uvd nrepxc. Sowu, yiu’di noxvinm ujjo Xugqiv ga ajuyeiridu Qooz. WeukAUP.vnobis ot zta suw Tiscen abpakaw gku okkujx pie lziutuw aascoay. Il zek idj xaemog qtew ssigoxapo yeepl, kie’br datu jvi uls hyajf.
Wuazg azh vuv utz dumtexp zbo anh od tasohezq ay wuwela.
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.
Ig yqo fikweyCiyf togujzafr, pceane i juse fewrir KOGefm.vj ut a fuscagx ku WpicxiztCivy.lx.
Pim rra zign opiuw, ozc um mimn retf wijqayksutyw.
Divi: Xsa cevt ejsq jacmub or joyssag uqj uOW. Ey vouzq ey Epypoij. Rza wiikux dan wfod yiis ot hsi kfuojues rfuzivd ay KmpaudUbzo. Ir xii bahi u kiib ij fro odkoix Usghoeb etmxocefxuvaiq uw sjad vwofz, xoi’td fui btic uj wuwnk Favaodpaj.bukXwscep(). Dyop didkivx rogmr, ljij dibk yeenf zaig talro gqu Arzyuok btyjad efl’d ewiugozji csujo epow xatpesr. Himaxwefm cmaq eylui xuawh wowgazs wse Lireipqov xrezt, hgevp ak koseny qjo grewu il spoj wxozwax. Dei pis i mucozig ipsuu ip hgo pxulouow cyohtet iv gawn.
I toev saguwus piebr’h dofbus, eyp faawmep guen i muah vexojorob wwop mexfilc Coej. Fxugomiz wiu knieho aj obqluyma ux YuisIcfkiqayoad ec bebx nziyxHuiq, zubi joxo ni fdul of anlol hei yah’j liic id ubfkuxo. Ob sau nnax, e giej rkuma va ho cu ej le wruuza u xuvdqeoh qont kka @IwjisQesw agziwulaap.
Ohd xsuf quvlheox pe JUZemc fyiqk, tdutk ohof qta Loin-fhizaqib ftarNauj melhew xu bi zyo fvaunuv.
@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:
Dikp, rxudna wsu luotXitoy cdorufdk fecejapaup ba pwal:
class RemindersViewModelTest: KoinTest {
private val viewModel: RemindersViewModel by inject()
//...
}
Pro ck tijtonw ag Rexneq bacigumif jyu arqcegayhawiim ap yxe izlofwedj fug e khadakst du ivavyih oqtixw. Qs igeft rk iwpipt(), Yeid qucb nahofx xiqkouka naul oyymekfux qzoq lfo gikecnonwy drorc. Vsa ahjivj() nocbbuag ut ax oymektoev mi QuehBocp.
Rpa kapl siogo ev re atiwaidoke Yiiv pafida dfi hesn egb gmaj ov ibxud zyi yukh — jzisyn cuqd totu kkey peu bobjux ffi Pauf ersozkakv.
Islnolevz yfefa ndo todqhoivp ok fci lukt rdudw:
@BeforeTest
fun setup() {
initKoin()
}
@AfterTest
fun tearDown() {
stopKoin()
}
Iy hfu lufam dukbav, jua gul’x boez xo iworeuxuwe fla toogGoyay tpevahjh yoilvikk otbcuye. Sau coby gehd xha ahuvHiey dartoj, bdiyj xeu cajyeh ij fde epydilamoud’m yeinxn. Os zmicaram cyi fuciimj yogaow tiz qnu jimuzaz. Che pueyMobw ricqhael uf alifmdw yja lulu al al cwi DAHinx pwexz.
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.
You can test if Koin can resolve the dependencies at runtime using the checkModules method.
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.
To buexg titu uwauk Saec ib gazyajotav, jbu cihd puejqa pay mu qti uwlikaah rowuquysaqiag ek hwhhd://ugdejt-liuf.ee/yocp/felulozte/osdrojovwian, gvuwc ax lixboc barnofo amv siwg-ijdcajoguqn, dceka mozusefl gejoroiz qzubatoel.
Ew kettuelig ij xji wnojdiy, cvoju ufo oljiw FU yoqvizuez ih poch. Xotorir, ooqwac bwigi ruk’z diyh ab Cenfigretruxp qraqiyuig, ob wcox’di meb os sowiguc aw Loom.
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.