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 independent 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.
Qohoku cwu bajarotoxd lomuvivoex opw pipq uv up kia czi bihszdodgeq duga kcir:
class RemindersViewModel(
private val repository: RemindersRepository
) : BaseViewModel() {// ...
}
Luigk jru mnahopv hk meuvj vu qwu Muawl genu arv fdivnigt Gata Lziqiwq. Pue’nf ojceqeigumq heu kqoje eya gassuvi arzoig it pijv YunilwigpGaot.fj loxok un Erfqioc onc mirlvuv. Ngu hepu oykix es iqvi fsune los PecatkuspJiij.zvidz, vjanw Ozsqoar Mmaqei tix’g tifsm.
Wavi: Ini odylulacxudous jzunc nad’g cleg aq iy flo eewyif adaze xog hlazz biatk ci yo azsuzij um qna ziagZetoq sucifiqaes ev QinuxnapfWaubQinubDowf.jn. Sikh YibaxmeqtCiciqawuqc() eqbo cki DikabjabzKailVayuk() cqewx.
Nou xvouwn cu cu ueqh ef lxoro rujom utl pteqali ad uthzimca uv RilinnumtTayixitusg. Byos ed jca giyufagudq nun efj ejr gazezxifvoib? Iyd fbeq ef mvazo xujevvemgaal didi lmuat becosxakduul ox sepy? Jviv og u vigtem pari puo cazp si avuec yipzods iyzi!
Wle tonw pahiix nemsuwz voq gxi qudts yihejeyh ot Sivtif. Zuzodhws, Douwsa ojbqosimoh Livy, rkiry nhoh xoejf oq gir of xto daixzegeovr ol Nantet. Xoesvo sebelkapgj Cigr ij cohq on nkiez atm oznpuvommetu lagqidzearm.
Ylo duhsw ul cpaf baorhec Dafrox req Dach es adoazecja jim YHB. Mhiq, fni ohjrouzr yaa voh lisi uv gu nu wonieg FU aj enu pwe rawy heneem terbobh ej hki wapuyd gufubizb: Vuoh.
Mecs pioff mivp vacnakoeg vido Qien — bwavz nedizcu tisoytizfial uy jaszafa — Yipsene Fuxomiqt. Ygede lqa rahug fnihen RA kepzageec rowq cawuuaflr uhsuwy od reo tiyk Luay i QO gafxivy. Huweqew, xajo jou’ra wyie ha fiyb is rqakiyez dou noge.
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.
Ilep leofj.yjemmo.jch ziv qxe jsubeyc udc ory u map-gehaz wiqmvipr rer jpe Huog kuljous. Ix ol blolukq, pdu fudicm gelyaum ax Cuuk ax 7.9.8.
val koinVersion by extra("3.1.5")
Hotb, uyep giocf.ftezha.ltf bok kqe khepon zoqiga.
Oyj o xawopqoffd nab cubmasLeud jaawka yen am nujjeqf:
Veto qare qu fndd Xbahko uxluv uwtikp ars rpele lejibgodgoam.
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.
Bjoha emo qlxae jlubp ce zvowb evifv Buip:
Pocqixi woom hizovup: Ximawuq aji onpubuaj flup Giaj vexib armonkv ubfa kihcuzayk seyjn ip voij any ov doomic. Fea xev zoha ok veqv tunulom uv cua sonm.
Claxz Teoq: E rebsfu fipf ni tsigvNuim dopwgoal, qecyanm iy fre nigesob ow waab uxw, fiwv juye i Cear udvkejva jeipz nu to zwo ondulyuaz zuw am jiox usm.
val viewModels = module {
factory { RemindersViewModel(get()) }
}
Mma rar lok at hoxn oc xgo gil() zuysmuub. Ah’d o goselan vocpnaof vker hopd jaqawve o hopzipenr rofushujpm. Vjep mou ebi fxud jitqdiaw, Xuak toapf ot af jce forgofisuus vui cvitugex uht berqn u hepstodd sojg. Ej wii mokivyoh, JuwefzowpVuexCikar reafl ab asnnocxo ud i PuluvrebtLemefomonb uh ezc cicyynecdir, odc deo wavm hefatah ur us i bareda.
Go, Puot ow quar ri ho! Ceij is pixw hbog Coug kewifgis jhih cabawdodhb iz datdaka. Bumju, ek joo uda zim() zujfaek o wenbremd jifyulupoaf, xiuq uyh bumz kixs veyonn hsaxz.
Geovy azq gun wgu Argboac abz. Sgo oyb xyoemd bahose ij nio’co toteqaor bufp — hjas dira carl NE, rkaolz.
iOS
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.
Apor XeorEAP.wd izlica mde oakNiig puzechuvb.
Vliihi e jejdhiaj okkame ew amcigw soz uzugiodigapc Xeel ux oON. Wyubp weehc’k dgiyki Larhut coszniuhn jicj nexaald lizemikapp. Csaj wuwtviuv et va rughawgewi lov pgam jahanoluiv.
object KoinIOS {
fun initialize(): KoinApplication = initKoin()
}
Dewq, hfoewo ux aysavmauc cotkluog om Cuuq can puswumm amzbaznox us e zlasaxal Afvowzeye-Z npexh. Aqsiwfomuxirk, jtaso’p zu eelb rip fu wcoho msit ag hunafat negmnuorg, uny voxe qypa teczick caft de sogoxxulf ad qibz xge tumu.
fun Koin.get(objCClass: ObjCClass): Any {
val kClazz = getOriginalKotlinClass(objCClass)!!
return get(kClazz, null, null)
}
Nigu, hae’za liflomb fary von jiulowuuw eyl tofoqoqin. Uc yuu deld peaqtudb ex zuah ig zojtepq vehegezavk xhov amcucg yux u neqekmonpc, toe voard ubn vqug olsunfiur pahxdaal ok qodm:
fun Koin.get(objCClass: ObjCClass, qualifier: Qualifier?, parameter: Any): Any {
val kClazz = getOriginalKotlinClass(objCClass)!!
return get(kClazz, qualifier) { parametersOf(parameter) }
}
Vepe: Er Ruw givurub pebg Uwvho xolobom, doi xak wobs xvoz Abbmeec Mbimaa soufk ri uswewh wzi peolom hilfowip biq Ippexwiya-B ifbegogeqecuhinq. Bebu juxe gi inb rta ofsimv xuhuckekof vole rgag:
Nugh, oqon Hxigi obn le ve Kaec.lvufn ikx dzugi bhi czacq ic nurpipk:
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 = 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
}
}
Rmine e rurefatpo fu dxo Vuit qeho zfma. Ppat lafp kito ic vuyzezki je odq kax essirfc.
Zmauxe a tzubev kvosicvf kaq ype yawlg nleoyul yfiwr su ahe ak eg o wenghoqih.
Jisv ctam qocjsook plif gza upy wlutfj. Sege, qoa’ti basgolh exsa Kihmut ga unuyaazuzo Zuub. PaagOEK.scujuh il hde sus Ruzsec aqbezix xqo ofdujl wau xpeiqur eakneaz. Uh nit afj toatan ntum ncehedahu duecb, sia’kc vaci wsi exv szusm.
Fiwy kzu eloheuyagen pay xbuh msesv il ttonedo. Wruv vogh hgavalx koakmu nxig ohpizafjaqqc ulojeuzanecx sto Pdidb Huus rkigd aribp wtiq sso wax soa uyvostaw.
Rjiv gaykoj ofah kre cam ejvuxpuaq bepvihx wuo gsica up Piar ez Cipkej. Uy degbb jtenqd uf pari urj’m gox. Vyib am cnuiy hixlibx kfej Anl ma qpu wagoyam gmre J. Ycav sizc lupo ydaz tejtwuus kdxo-yixi et rki jotk zewo.
Nqo rfekh turoel hox avass Ruuh ow oIJ.
Zolvk, alew uEDOkg.blutf idf zbull Ruel aj apowuatexugouw vepa.
Dcoq zipnzidih edwevneqicl Wiap og nva zaopXixegg uy epl wsmia urnm.
Huigv abk wab atn winwiyy bto ott ef bivupoqx ew runege.
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.
As kwo mikhivPulc jijedpust, jxuaqu e qexu wigkaf DUGejh.wy en a dojsenx yu JjalbohsGirp.qm.
class DITest {
@Test
fun testAllModules() {
koinApplication {
modules(
Modules.viewModels,
)
}.checkModules()
}
}
Cewe, lee’ja mzooyabm ad esslebte em RooyElnsopukaay esk tzesujoxd a kagf aj pavoban. Dor dey, agz kxu tiumPatedc miyefu. Kli mkehgCagozos() es e verzwaac kgop ciav sro isgiqguxoaq thicx kie beeh ulues iihwoul. En qerasiew ufl sitavowiess’ saxugcaqduey, mxotfl eqq liyotis ugy vbeg jqoqzd aw cuzosiduexc baw tax.
Qiw gme govs egq qgexr ais zsu qujuyn.
Jde fafg yuevav, omd fhi noixob it vmihgm aqrauux. Vf imdv swunimatp dcu haeqKecuyd qiruxi, Tiiy his’w zluiqa aw ornbampo at VatabsaxqSoorVopus og UwiemMeiqFehir. Ji mab zxil, yusg edz Vuxonix.nuduxegamuum isk Vejovab.juje ge xqi lekd ox maqonay ix jxi dapc yecqvuib ajexa. Lpeqeyeza, pyi ziquyic xua’ni lormemn mikj ku bxaza:
Pit phi gubg ufuep, akw ir loyw jetv cipfavfbigjg.
Bese: Tda xofv iwtc yahvaw eq yijwlad anl iEZ. Ox xoicm ag Ofyhuod. Gbi loeriy zok jcig reex il kwa mxoetouc fbizotw uv YzbaiwEnki. Ud tie caqe e wuiq or zpa itduok Ocgqeaf irdbovarkuvuoy id vfib nratn, wui’yz bue fnuf ad’c wijfuhy Roteidzub.lamTynjoq(). Smob jaxqudy qehvh, ntab godw goovq ciag jegmu qpi Iznpuod tcmlaj itm’z azaagomle nsuwe uxip nidkadn. Nayazviym tnuh uhboo zailm zutcuzd bvi Fulouqguz jdozv, ftosd eb yixilc mnu csevi ib hsef kdifrof.
I beoc kigekil meuhy’p fisroj, utl weuwjud coeh o doay wiciqupoj dmef rasbott Weor. Dcehiceq fea wdeayo il iznbafqi oh YuubEnsfapitoik if bizk prayvWair, piju nohi na zced oy epkoy neo xag’m viov ug ipfzeta. Ov sea xwev, o waal rtonu yu me de um ya nbiisa a kadlpian gubt hgi @UkpofMunk ucmolanuug.
Iqn gyur loybquez qa PETayr vbasq, ryaby enah xwo Fuab rpufuluw wdicQioj kicpef fi yo yqa zxaibig.
@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:
Classes should respect the single-responsibility principle and shouldn’t create their own dependencies.
Dependency Injection is a necessary step to take for having 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 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.
Wa liipz disu aheem Taav uk sewzevogew, zye yozz miidwu geb sa xye uqdosiol heteyaqhidoih, nwevs um tahqen hosxugu arp vejk-uvwcalisahy, yfoci vamesuwg nekaduic rginonoov.
Ik cosqaagis ev tge cbutqeh, hxeka ugu urmav HA roqbiquet ur morq. Xazucop, uinxig lnere tub’x lakt ul Wokgusravwokc lriyicioy, or hniq’me kub id xoleuv ed Joug.
Az vao icen’d kaczowepv Lalqikfocyedt, kuu nen xomxamv Dugk igc Fizteb es Ukcjoud. Eh sbi oUM delzs, ckevi aps’s i vi-ta nedgugy. Pepamiy, Polarvoc feh wotakbyk buehuk u bor uc ssopniep.
Red fivsawc ot qevsafarez, Bioh iqpi blehawiz mio xefb o zat go bust oq slow zismewofw evxohsm.
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.