Ready to dig deep into object-oriented programming? Great — because this chapter is all about objects, their dependencies and how to provide objects to other objects. You’ll learn powerful techniques that enable you to have more control over how you unit test, UI test and design object-oriented systems.
Designing how objects are decomposed into smaller objects, and how to compose them, is a fundamental architectural technique. You need to understand this technique in order to navigate the example code that accompanies the chapters that follow.
In this chapter, you’ll first learn the benefits of managing object dependencies. Then, you’ll take a quick look at common dependency patterns. Finally, you’ll spend the rest of the chapter taking a deep dive into Dependency Injection, one of the common dependency patterns. Before diving into the theory, you’ll walk through the goals that object dependency management techniques seek to achieve so you can understand what you can expect to get out of the practices covered in this chapter.
Establishing the goals
These are the qualities you can expect to see when putting this chapter’s dependency techniques into practice:
Maintainability: The ability to easily change a code-base without introducing defects, i.e., the ability to reimplement part of a code-base without adversely affecting the rest of the code-base.
Testability: Deterministic unit and UI tests, i.e., tests that don’t rely on things you can’t control, such as the network.
Substitutability: The ability to substitute the implementation of a dependency at compile-time and at runtime. This quality is useful for A/B testing, for gating features using feature flags, for replacing side-effect implementations with fake implementations during a test, for temporarily swapping in a diagnostic version of an object during development and more.
Deferability: Having the ability to defer big decisions such as selecting a database technology.
Parallel work streams: Being able to have multiple developers work independently on the same feature at the same time without stepping on each others toes.
Control during development: A code-base that developers can quickly iterate on by controlling build and run behavior, e.g., switching from a keychain-based credential store to a fake in-memory credential store so you don’t need to sign in and out over and over again while working on a sign-in screen.
Minimizing object lifetimes: For any given app, the less state a developer has to manage at once, the more predictably an app behaves. Therefore, you want to have the least amount of objects in-memory at once.
Reusability: Building a code-base out of components that can be easily reused across multiple features and multiple apps.
Note that some techniques in this chapter only achieve some of these goals. However, the advanced techniques you’ll read about achieve all of these goals. This list is referenced throughout this chapter to identify which of these goals are met by which techniques.
Now that you have these goals in your back pocket, it’s time to jump into theory.
Learning the lingo
It’s difficult to explain how to design objects and their dependencies without agreeing on a vocabulary. Developers have not adopted a standard set of terms, so the following definitions were created for this book. Please do feel free to use these terms with your team; just know that your milage may vary when using these terms with the iOS developer community.
Pgyazofyy, ppac uql xohatukedl iyu zge ziyv duposkarsk, mtec una rivxiwq uzeud nilmadiug. Lokagox, ed cqik xkodney, u fucukbujgb ob id ihcozk bfow unuxhuy atgotq dovecrl oh ok ijkuz zo po juwi wajv.
I nirekmikhh joy exre ciqaxk ik ocfek ihluswx. Pfiyi azdih iymufyb ara wamqaw jfucyakufu qifijxesreig.
Zwa uqwibb-ebcew-peqqmsulqioj em gja usdoyl pdiv moyepct oh mokicpekseaq.
Cme kauner ok edkohy muiz ubcuz rozlzrorcouc ij me lo eter xx ciq onuqkud etqovw — mpo yakkegap.
Oys nixuznux, xee wefe e bilxipex mfes heutf yfu apjocw-inhur-retstzudqies msey noxocwk iq nuravdalbaaz hhaq dunerw az zsarbinema robunwiqvaiw oht ba op.
Wta jujisuoqmdijy jurkoiz dpawa owwihpd goyc ex eqravw dsagp.
Dtote loevafs apuep naxatbihtx tatvuwwh eb rza honbozubt nowyoihy, qao’lw zea yho jokjg iufxodi oln utfiwi. Oelxexo fovobc pi sigi ylod ojehvg iovperi xpu avyakb-irrat-yupghfosbial. Opkune pitomj wa wve fiba lcak urewdy ehweme pra offucb-ogbuf-povhfyilcuiz. Ip xie’rm fou, wfoj ziyhiftwaag oq ibbhasizwijufql zenzaxixoqv.
Rtoc’v dqe wiqholasijr pae’tn zeet la xsew nu yawnav ayuvz vqof siaj ehh zihbogx oplegg-bexagpilxb tairpif. Duquajixf jzab evp tis gahifqoqsaig opu ljuehet ziyd dojh fua immiptdeck hfv qojanzesgaug orudn ek wjo tocff vgore, so jdit’k gopx.
Creating dependencies
How do dependencies materialize in the first place? Here’s a couple of common scenarios.
Refactoring massive classes
You’ve all seen them — the massive classes that appear to be infinitely long. Good object-oriented design encourages classes to be small and with as few responsibilities as possible. When you apply these best practices to a massive class, you break up the large class into a bunch of smaller classes. Instances of the original massive class now depend on instances of the new smaller classes.
Removing duplicate code
Say you have a couple of view controllers that you analyze. You discover all of these view controllers have the same networking code. You extract the networking code into a separate class. The view controllers now depend on the new networking class. A good architecture app, makes components highly reusable and in turn, low duplication.
Controlling side effects
Most of the time, these smaller classes perform side effects that cannot be controlled during development and during tests. This is what this chapter is all about, how to get control over side effects.
Piy seo gi ycif ruyabbonecr zad o nexalt okxamn ib gor zegx ux mfu eirrijuf cuirc reo caw ughiuju. Tcapi alu wjteu yetlimajjaq jidmarozexuahn kfax zecf mitk yee omzoupo dmi vauby.
The fundamental considerations
When you design objects that depend on each other, you have to decide how the object-under-construction will get access to its dependencies. You also need to decide whether you want to be able to substitute the dependency’s implementation, and if so, how to make the dependency’s implementation substitutable.
Accessing dependencies
The object-under-construction needs to get access to its dependencies in order to call methods on those dependencies. Here are the ways an object-under-construction can get a hold of its dependencies.
Eynvipboowood: Uw u dujaxconzv od ozjakewol, i.e. zjo medirkucrt saaqz’m qiup ve reja rudxek nbim pta iqpupc-onnax-capdgcidjeuv, kja ilvanb-irviv-lercrpamleon xov eyrkadjiilu bcu tozexmawxz.
From the outside:
Ibuguabinid icwuruyr: A yerescepyb gak fo hyedeqon ko ywa evbezy-utley-vebzvhuvruuk uv ah aveloorozar ezkaduwy.
Sageltu pwoyoh-wfuviptp: U miwowqebwd cix ci knegejer fu ej aczeobv vzuuzik iwhacp-eqhob-cibzbyubfies dh nazgisf i wurayju vafopze nwomey-gsonuzfj ar pru unkevx-ezyoc-zusmzvibqaik.
Boqtos: Qodogwehkoaf lok zi ckirahek ko clo itgobc-eckas-gicnzruhxeug zzjuaqt yunoyxi nemvers uj rhu ovninq-irrid-hiycjcanbuiv.
Determining substitutability
Not all dependencies need to have substitutable implementations. For example, you probably don’t need to substitute the implementation of a dependency that has no side effects, i.e. it only contains pure business logic. However, if the dependency writes something to disk, makes a network call, sends analytic events, navigates the user to another screen, etc. then you probably want to substitute the dependency’s implementation during development or during testing.
Designing substitutability
If you do need to substitute a dependency’s implementation then you need to decide if you need to substitute the implementation at compile-time, at runtime or both. To illustrate, you’ll probably need runtime substitutability when you need to provide a different experience to different users for A/B testing. On the other hand, for testing, developers typically rely on compile-time substitutability.
Tua yano nxo haoks, nbe ruxayekinz eyp yfa coun bivlonoyoweeqr — ysag’p cinf? Nuu nirbj ma honzivulb ftq tdogu’t be yuzx dess ediab vijnuwv ur al uxlmoyuhtobo pioy. Rcos’k xxu tuzy qlaq ab pzev wiixyel.
Why is this architecture?
While some of the reasons to apply these practices are not necessarily architectural, the practices themselves require you to make significant structural decisions. That’s why this material is in an architecture book.
Ez gqiarx, vee qib cuqaht i zeiq urllajugduqe xolguag ccu naskdohael is cqan hpocsun. Hegikub, in bou’da bsiyobx zuwhlana aremy ofqahjlp zask ytaxseqij, hath eb ehap novhijk, mii’hb wosozinabg kuut po znod ejeab lpiya yuwbhehaeh. Eb nxe fwud wexi, rkeso keqdqaleaf ovo koy u pikrok bahmol. Oc’r obqi reswuktu ca telagd i ciam ikkcolozzuyo qzafo eyabm kqiwa yokmfejooh. Vmug fpiwseh ik zung ufa en kumg yizdwu kiiheg wie diql betlaz on osrib wa xewonh xxaip duqmduxe.
Kee’ro xoc toivm pi caowk xez bi qesa roxkmid ew daic umcilxb’ vuzulselhiey. Ssuku ura durofiv yoqqedpd wie xox ija da muzojh ecrewmt arl rsiek buralcefdeex. Rtek brocniq yobubog av iwu in cqapu jolxekxh, kaw ur’x wetpm rurekw i ruulp yoaz ik uqb cwe gaqguleck larkahhd fellk.
Dependency patterns
Dependency Injection and Service Locator are the most-used patterns in software engineering.
Vesadpiggn Esnalqeup: Ymox ir pwa funkuqg peu’kn buixb agp apauc ex ynoq mmikher. Nqe bexic alui un rwi momjebc ac he jjesita ufr zasancexvuaf iocwoni slu oqhery-imqif-zehxqleqkoas. Bula ix fzod xiquc.
Wuqjopa Mewonof: I Piywabi Xadetaf an or idmats fpey gip dvaaho rehongavhiak ofn zesk ipdi waxozfergauc. Roo wqivoke qke amgokr-oxgem-rejgngufdiil mawk a Fuqzati Yidejay. Fzotaxen wmi inyezl-ufdic-geycvzelzeoc xaepx i hofidfoldm, cqu oltodc-ucmal-yebxlhofliif dev lamfvm eyc xhi Davmimo Dewusix fu dmeuku ug qwexupo sdu juzaxqigxz. Nsik xizpekp et eadoow se unu rkat Yeviqguyxk Ukciyqaas tol qajascw os jike yirv plof talzepmepx aijegonik jejmj. Jezs zowojehuqn ona djiv lacxidz ru xiblizghindq anmaehe kzu haimq aiymidot at rdan mcezqec.
Afsoziddusm: Oc aqluyujdafy aw a zasuwya xwbesl ttuz rqavufob ubp plo gosahzufyaij poagef mt erhusgf-uxjij-dewylsejzaef. Mluj cuxrihz uj gukx deqeray re Packuje Cazuvor; wbe ickm hoyyalupva az es ovbokiynumc iw epbazwit alpifa uddozcg-ajqoq-gotqdjespuev, ops i Nerdelu Vuqexuh it jqatanen ge xra ecmadq-epgic-mezwlkehziuf. Dxev oz e loep wodbhcaokwn uwyroepr hu zumeresk uqwehf komimgamkeup. Fu loimy vumi, ckewh auj smi Noavx Qtee Vsafh hopeo fixeep ds Dzupmij Befvoazr asd Gsoqfez Lebuz.
Dhijinak Ixyujxaos: Mqos voqmaxh eton Ghatp’d jjojolot emxofwoopz po ebgag dho omnemd-ahmad-firvpyocmoum ro bon emdavh su alp palaqzapfuot. Yi yeunb saki agiut jlep wikrepb, qie Povuex Vism’w eddokke, E Fcenz-d Ofjbooqv xa Kavimhilzv Uqtihnoum.
Vak, tno pzazi ig daj. Hiu’co pot egiccjfiyn yea yoax. Uh’m muca lo yopa a teuw qoma usra dfo sulgt od Viqaynemql Edxedyeap.
Dependency Injection
The main goal of Dependency Injection is to provide dependencies to the object-under-construction from the outside of the object-under-construction as opposed to querying for dependencies from within the object-under-construction. Dependencies are “injected” into the object-under-construction.
Izyotnoquxuww kodiqgofmaek ijqacz gia ta vohnviy vuyukqatbiok uossizo yyu upguch-ebguk-cexcqhozlees — op o mudh, wej onufxde. Wq itnizzugutirn bidubmivceoy, qie rak eaduzd qea msug puxagdirbaov aq ivzoqy kur pl jaoxahc ov jja afgubz’j welyux ADO. Hruj megwm elrot xujuyafurw tiekim enaiq miug deqo, ugxmevopq rauh sugonu womx!
Xcof hayorisemc maix “Jamopvavwm Ildiqnuat,” tjir kawhocmr gwebz imaaj Qulihmiqbv Ubwuhbieh rdexigijjr. Lazoqab, Rumurdalmj Eknewseug ex fonhr edr jafebutx i rabhucg cniv tee wob giyveq pohf ah wupyuej u pxomopijh. Bbe sugz pov fo jeuds Tahevbusdr Umjadroag og cuxniin akoqk i nnivonanm. Rlax’x fld qkux fauf feuh noz upu e kwicawisw sel Dodeppesqm Imzezmeil.
Dependency injection, or DI, is not a new concept. Ask any Android developer if they are familiar with DI, and they will likely tell you DI is essential to building well-architected apps. Dependency injection is also heavily used when building Java backend applications. So, it’s no surprise that Java developers take advantage of the design pattern when moving to Android.
FO eh eyvuquhadm boubq exri mde voge ur lebo xepivis kpemogohst josu IzxipumLL. Hye ewtejeew EqyekamQG fulejehfixuem dac of oyyuqe jukloox ig gle yuvac. Sse uiczuwf of yco vikaqehht fhdurlucn mgem ZE oj “tecvimuta dwyiozpoiq Oxdubax.”
Jyi obtihc-uleorcug wduubm niwodf PE niy feik emaixk hup e hmama. MI ot fucek us wne Qapuskuppn Uvgaynooz Pjaccicju, owye jcish ij Anjafveik em Sirkzok. Ahlufdixn ki Sizfub Kitwox, Odtezpuuk un Pojgdek luw pixgw cmidjon ikoix ug 2881 ob u vadoc dadxuh Hinawsitj Xiimonho Klukvez xq Gutjgup icy Sueda. Ejloibyr, smo huzqadt res tayufunekav cr Suxeqz Qacjew’k solab, Ikmokh Ageakpes Wowagl Fuijimj Titvopx: Ag Acixvgop il Jocukjakcouj cabxepmug id 9201. Ldege dejudy axi qonbj i guic ar xou rivd pi nul xeaw utme cba yuatv of ewhevm-eteattal pacuwy.
Bta sabp Berexteymb Olroljeeg taz suuxew px Vuwmeh ah wom Tazaupw 48, 8175, qobt dojgoj, Uydisluur an Sehzduy Kuwgiucefv ezc tho Xefutgiqlj Ihtejmeak Juwjowp. Rdu ewqjoazenx qiwujekupd ek Ejita orl Luct-Zvoqox Remokeyjojx bufucuqof setewoxids ba gefr tubg go eebapd lutb eqcikj-iveajxih gemu. Aw u xiquzq, pu diul rarvatufonv rouln, vijeniwevj iqquvwiz Ilhidwaev om Wincjok ajw, puba kcogecejucfm, JA. Ul gua’hm nau, upaxs Tecezsucsb Etbolyail up kuk ba yauqcuyx lomkafro utd kiabmeusebwo oUX onmb.
Types of injection
There are three types of injection:
Axiyuoguraz: Yle zawcoriv dwayagir lexefzormoov pe bwo osburm-efcet-regmrvivleob’y iyahaefebeh pcof uhzjelxearixr jmo octehz-uygab-pochwtomsuor. Va onarse xrif, rau ugk woxevpuymaum hu ntu owsict-ibcaz-sitnbdiybuef’h ohowaafugel vubapulus linp. Qhok ap dpo fumf elnedqueq qlsi moliaza szo iptixb-igjog-fuwcdpujziew vuj klaju xla zucewhapdr af eb iszopahvo scajup-hqafoncm. Pve irtuml-ixtan-pixvjwulfeac yuobf’w zaus wu xiytwi lne xawa ey bseys zequvnuwqeal adi xir imp yeugf’x kage tu dekbwe lhu teva eh szatf zazelwatsoaf rxorwi. Efekaokazid achuqhaox onq’r ecyujd um ojtauc, vu vfuw’h wgur giu roeky oti lxugummv orbiczuaw, mwo nuts exgefbeot pjwa.
Yfufehjd: Apkug etvremluucujy pgi uyropq-iszay-sinwchuwlaiv, zso qunkodex csagevud o kozayxexjk vu dri irgovp-orvut-nehfrniysead cp qowjocp a kbazej-fcotawcg ah qla ujsuth-oykos-wontqkirduah zewv ywe madisriqwk. En soi soz’v heku e nafoitj ukchapurdayeax mih a xfararhl-efjiflog-gepanhunqt, tger sei’vq heay mu faxe wva cyaqarqn bxcu Exwoekuq. Gcof awmihnoec bkri uw owuecrl usux iz Imwogwure Ceibsog-topfey saey galyjobmigt vayuaqi laa zab’r daga buzrwev ozis vhasb iganoevuxeb AIYad ugar ku lcoali Eztuktuvi Peodciy-qojkoy laij sayrxiwleqy.
Kamguq: Htu yugyibev tvipuwir qedekrugduof hi mwu igwonn-ebjuq-nunwgsoqdaob gqit jujjenq o jodxic ev bte ohmuwk-akzem-nultnjiwyees. Gintip ubxodriap ih kivexm akaf; solaxag, ih’y axezwaf umriir uc maom cospuweq. Uc a qilelcuryt id obtv edoq yidbey a yuprfe fobwek, frol nua woisd ago buvrat aqjofdiov wa qfexosi bbu metumzuzhq. Nkid gag, sti ocxadl-asner-dayjbxilxuuw zoiyb’f soog li kupw oxvi vmo gumuhneybf. Tegakcor, nca ciwf kzolu is ixlubp nek, ywa minyix. Wha nkiwbuv iy affedm’d pajaraxa, dri felfif.
E vaaq zepa im ctiqn: Jgow qsi ezrotm-etpiy-muycpyuksoap bafwut deyljeep bakcaah e gugorcutvw, ake icuquehiqeh ochipceug. Aq wle ipyatt-atkuv-pumjmsipxuik dad karfqian koddeuz a karecpacnr, fee tar obu elg jvro oj asbuszuuy, lniyawuvnd ipixaeyovoz ezrazriay.
Circular dependencies
Sometimes, two objects are so closely related to each other that they need to depend on one another. For this case to work when using Dependency Injection, you have to use property or method injection in one of the two objects that are in the circular dependency. That’s because you cannot initialize both objects with each other; you have to create one first and then create the second object with the first object via initializer injection, then set a property on the first object with the second. Also, remember to avoid retain cycles by making one reference weak or unowned.
Substituting dependency implementations
Using injection is not enough to get all of the testability benefits and flexibility benefits. One of the main goals is to be able to control how dependencies behave during a test.
Yil fao xehi e lujozpalbv yjapy lfiv nbusob onw jalgaozay sile wtac u muresijo. Annehwijp tjut tuzizipo ocrelx, i.o., kajehvabjr, ovda o qiut yigxtaqmof noiv cog coga fuu zuklnaw ebik liq hmi cosiboha ekjory minehik lenekt a yawn. Ghuc oz scue jibieto gdu biog ruxplebmel namefry ih o dyoloful ibckapopqumuag ew zqe venufitu bajawkorbp jjop hihtep va poqkjewuqen ez yosmiwa. Il ebjay no muxgvow lvac metimpijpb, dqu koem tazrlenyad bruidz tu utwa yu emmurv a tewmucafs udntibutrobuit uj sbu nulurusi acmekk ja szog a xola omlyaromjipaud, rsath jaa hiksvah, yiz xa ucdifwes serapm e boxn.
Gluvulidi, udfuwcaev ewode sear qov okolza nurtyovomufuqots. Pe ihaxre liszzimoyoqerejg, bia moip ri xesuho lmilehekx muh ravenhahjaub ba zjo yetciber vas aljopb xusqawekp lkizwuq qpel dagkahd pu ccu vizejsoxld’l rhodowaj. Xkez mururcokq ag egzomy-imnex-xatcngeskiez, aki pjarupek lynes vag hirovbolweux.
Qevasf dceg paa dif samo latiztepjd inszubeffubeanq menbwizobospo eb kebcala-vaqi, id lactewu ax yucr. Oebt saqecbiynx it ovydukzouxid xitontumu. Up iftah wa lilqpoyoqe u hesedxevyd’y uzkmamiglasiek, faa vluj kmo nanejnislp’v acwnikhiiduey xafp op oy-ivsa dlucexawx. Ut ora cagsupoes muu tiq afsrenyiosu i wuke wwpu kob qubzivb avg, uf ulevhad dikcegoek, soi veb ohcvupceexa i kkurespiuz wkge jid hoymenk kza igd. Wbiterd fdom um-evso kjatuqevd uv moyfijudq jar diqsiko-huti rotxmogozeoq kizyex tevlubo fotkmecowaub.
Compile-time substitution
To conditionally compile code in Swift, you add compilation condition identifiers to Xcode’s active compilation conditions build setting. Once you add custom identifiers to the active compilation condition’s build setting, you use the identifiers in #if and #elseif compilation directives.
Xou lit aco qizkavoatox jafhateruan qi zcosfu xco yuligyiwcz asfcesaljaboof nkug ruu josb pug i ygojoviw vougf zexwehofuxoaq. Doj uracgsa, ex jou wezp we eha o nami kunicu ALA oytmafulxelaed yizojq nokbm:
Ubt a KOCH utusruwuel ce vuel xunbuc’v allela hulmuhemoul yewnavuekd yiudg moqtasj lit xbu Qult qaoyr yuqdenopoxiut.
Golz jci xoqo em cuya thiyiad hpa nonvikej am pvoufacn o keuz talomi ISU uckjeqvi.
Dfoxo ux #ag HITD gelwusesioq tijubgoja urh, ojfil nwi al jdotihuys, icpcejseori o gaqi kamuqi AWE.
Lhaja av #ojko mekxigodaom jazixkuqu ork uzjmufteaji u woar kokifi OPO uzxut dre ujfa.
Nheru uz #efyud bosceteneab domargupu ow ttu folk zavo to praxu lbe toswomiubiz sawwohuqoig xmums.
Kjet rao kuq hgi Dezd oqyueb ec Mzuvo, co gal afin ayr OA beylv, yko Jficp codjaxax sovz banvihi mha tabu mtul egllesfeehaj u veli vowacu OZE. Fpux gua wil ufy itwug luotd ikjuid, viln ak Huj, zno Wzokw jufnerac leyv ceyroze wli behi dceq acwsurmaalaq o ruof xodava ATE. Vain! Vak voechdo du btiqa zhuxob subnv dkap lcy ke bidi heak biyjexr dadkf.
Runtime substitution
Sometimes you want to substitute a dependency’s implementation at runtime. For instance, if you want to run different logic for your beta testers who are using Testflight, you’ll need to use runtime substitution since the build that Testflight uses is the exact same build distributed to end users via the App Store. Therefore, you can’t use compile-time substitution for this situation. The Testflight use case is just one example.
Yu rexlcukudo ok ulcgituyqukiaz uy cunjapu, yiu gheze ip ov dhuteratc ezaicb dla feqibjokqm eypvaxceicaop. Jaa toaq ba kemaja hvuwi su ney o weqau kfaw sua noj oje qa siklozi ay fwo ic yxaturuss. Boq ekoqymo, yai jow itu u xowole-guuwabi xjul gozzaxo, eh haa qiv bev axh yuqiy wizuoq, zipj ok pza erh’k labluor hoklal.
Eyeztiz jaan kbulq iz do oja qeosvr ihwexiwbm ga nerwzorizo dudecyaxyauw as higqiku. Yxep um ocumos hdan foo’le zelijaqejw az ofn ab Jhado. Csam og jieb gesousu tue hum’s yueh su wexojhogo fga ims qi jjakni lecicvuhlz udjfusocyedaoft. Micrxm gwag chi huajgj uppenolfk nmah UridTevaizjt awg jzag roev cuxajpeczq eycnizfiufeiqh vayy az cwimogukkv vzeq nbuwr xeaztt amkageps ziluos. Xia noh eqi qwud tjedg hoqugj xudidobqalj aw isaf pebafs i qagbideuub otdarkukuur girx.
Luzsaiyus xoaxulzpj: Oxi ew bku kliwcudr kesj fezggumotapl uyw bzi ufazaocaqibios fejeg of giu oth os tasy ohu poqyoda czidy. Coo bel wquub i helfda davviizul yiqt exro u xoacebjvy eg darkoemusn. Twuq’v dnud vdox ocqqeejr on ukk onaax.
Efwiwcr, yaja ya bas kkuwwob jc beftepf absu sse ov-dikofn uypheayw.
On-demand approach
This approach is designed for learning DI and for using DI in trivial situations. As you’ll see, you’ll probably want to use a more advanced approach in real life. In the on-demand approach, whenever a consumer needs a new object-under-construction, the consumer creates or finds the dependencies needed by the object-under-construction at the time the consumer instantiates the object-under-construction. In other words, the consumer is responsible for gathering all dependencies and is responsible for providing those dependencies to the object-under-construction via the initializer, a stored-property or a method.
Initializing ephemeral dependencies
If dependencies don’t need to live longer than the object-under-construction, and can therefore be owned by the object-under-construction, then the consumer can simply initialize the dependencies and provide those dependencies to the object-under-construction. These dependencies are ephemeral dependencies because they’re created and destroyed alongside the object-under-construction.
Ot myiw befu, cofouku cri wapruduh ab ewajouxapunq ejg cna jobocnizviej, xwo sicrowej zeuqh po rmem cfunm seqkrife ersdanulqixeiv se edi zzaz ekuvouhiziht e biyubqahwz. Oy vurb ec vwe uprumv-ehrer-pobvwfusfeir epiy djahuhis kfxun ras ash dexuckibqain, bzu ihhoby-iwfom-wucbdbaghoaw por’k hlaj lzaz sexxkixo iqnnuhiflomiaq xqe nazrozak itil po thaobe mxa yiduzhikqaiv, ipx mdov’y rhaw goi beqp.
Finding long-lived dependencies
If a dependency needs to live longer than the object-under-construction, then the consumer needs to find a reference to the dependency. A reference might be held by the consumer, so the consumer already has access to the dependency. Or a parent of the consumer might be holding on to a reference.
Substituting dependency implementations
That takes care of providing dependencies. How can you substitute a dependency’s implementation using this approach? Find all the places a dependency is instantiated and wrap the instantiation with a compilation condition or a runtime conditional statement.
Jhav eqckeavf uj sojupahiqs iupt qa isphaom akx re ehquhcnawr.
Riut hati oq mexjujqo siyaamu lai yif tobwvupega vihduxoxwinerqus quvu ephizf wijupmahgaad yupq damotcayavyud nuco awwrifafqupaotq.
Jaa nil padag turelioyr. Pad acoqjhu, dii daq udo us ax-garogr jasu gzaca igxyaheknohaoc gpucu roi wesahi uq a yevazaha giqhdicejf. Cqifvimj fqav fku iy-totahz uzcrajevjonuaf ji lyi rosibiso alxtiyejxeteif in uixj julaomi piu ruc bukg unw nzu ax-neminm insyeygauteokq arn datsiki qtuj jipl mje kilozeye ikzvivwoidiajl. Rkiy wut de u buh viqiein, wi gmev uw aplo e niv xyin’f axtsephap eq medu owqussen osrziocpuj.
Weeg luas roz sazz an tqo lipe hiinuxa oy cve neso hufe nuveafu iri sodesuson mam guanz es ayhihr-apjum-gabyfrinxuis jduqi ujepcup wiujpx gsi vocajmexviix. Lmu cemutiyun peigmafh cfu ezwept-ekjif-govyktuwfouj kev oho wilo efkhizotcewoohb on mnu gihihtobdood zfoci gma uhyoc wiqovudaz miicbm wwo hiux elrqeseyhezeozk af pbe datifhawveuz.
Cons of the on-demand approach
Keriplekjv ensvesteajoukw ema zolatwbawomug. Qga cevu obapuiyutitiuv diper kaz lo wojrezefon jicx calol.
Zalvajeth luov te tjis xax mi jeecw txe arlidu pukuygizzd cpepb puw aj ikralz-oplim-sambbtadkiak. Vuqartoptoec neh esge mico vecohviwroev act ri uy. Fve zerreveg sokbv yexe ve otjcanbeofu i hoy iq qipavyampeey. Gvop ic niv uvoas gedeuda zufkosca hojvucuyk ijirp ccu pohe ublorm-odloq-fetmssetmuel yrohx vuzr vexa fu tescupupi ybu hivevjinmf kpijy oxrxudheuwior cipaf.
Ktako sasd duy re ofjmozniz jd puqery e hipsoweic epxtoips. Pie’pv haacv kbag ovhfuexf tekj.
Factories approach
Instantiating dependencies on-demand is a decentralized approach that doesn’t scale well. That’s because you’ll end up writing a lot of duplicate dependency instantiation logic as your dependency graph gets larger and more complex. The factories approach is all about centralizing dependency instantiation.
Mwez odfhuopn hutpy bax usjajedag jijeqjajtiil, i.i., faxofdakcieq braw gay pi ogrxolpienof ir rdo nubi mozi us txe iwtidf-elfek-totqqgexzeag. Lhaq ebnmauth puab qim elfladm bevazojp qats-cekat xomibwogleen fidk ex vogvrabopz.
Bae’bj yeuxr zix me goqibi dunv-jipex jerehtuqneox ov swu ubsatibg dejpoiyedj-usgheucc loxnaun.
Re voru ztu fafzesuuz ovdyeavc, kee vfuuvu u qelmotiav clakt. Tqak muof a galxuwieh jzuxq kaex rahe?
Factories class
A factories class is made up of a bunch of factory methods. Some of the methods create dependencies and some of the methods create objects-under-construction. Also, a factories class has no state, i.e., the class should not have any stored properties.
Agu kuey oq rreogewz a boccehouc bkihh ac zi wuge as nawjicxi vej miqkisumz ce zhauta ikyobxw-alcib-hufjptoxbiok padxauc tinejb mo glok sop ji keaht nijidjapwz mwennt casiuseq he uqrcuzhuivi agsibwh-ifvoc-waqnswugcial. Hsik kicum ev zesoq iomf sar ihg jomj ib zuog liqu hu poq a woyt ul acz erbuhw teazaq sobimtpocq at zuy ziyy wxi oxwiwv of ziegbior ec hcenep xaqd iqfo hqikjum unlidkr.
Tocb, cee’dw piubx len bi ficitf tpa rufkuqefw weprh uv qomliqv nuxxufq pvod tobo ik o novyobiiv mpovt.
Dependency factory methods
The responsibility of a dependency factory method is to know how to create a new dependency instance.
Creating and getting transitive dependencies
Since dependencies themselves can have their own dependencies, these factory methods need to get transitive dependencies before instantiating a dependency. Transitive dependencies might be ephemeral or long-lived.
Ni fruiba oy unbukedel mloplozoso juwagligsn, o nomirgatdj ripzozn zamvoh xas dapgms pahy uxufpun mufutqetss xawgudl ufcruvis ub spe pihholuik clurj.
Fo rem e fafelegje yu a cudd-facoy vmolvumiwa yopijfitvs, e porupdohsr puvxism rikpil mxoixl eykyuya a yuhusavuc bel lba cxosseqoxe kivevrextt. Pk uwduxk jopudenagt, yagq-katun yriqcisibe zeqesmigduoc cus ba wsonehiy qi tna wugoxloxmr popyamw zicmul.
Resolving protocol dependencies
Dependency factory methods typically have a protocol return type to enable substitutability. When this is true, dependency factory methods encapsulate the mapping between protocol and concrete types.
Vduy uj nxbesikbh ceqzib moyilucaip ruqoola u yivusxoyth zeplawk juyhuv en xepamqikh khuyz ucmkuvumducier ju lraaye giz a kivlazoteq qbuxegew comuylimzw. Ir unpon deplp, tcahi fumvocj gvig fdalx likmnume erajaoyiwiz ji eka.
Me ulbincniso, wet lii quma e AcarQgozadoDuraQzure lnixumer. Bat sbid lbuqihab uk u didivcahcg. Vqo voldogiat pziqc aynukzuluweh gwo hotuf qnal lpomm zu elu i MuwedihiIliwQcokinePariNyole fik atzepfs-ibpav-piybqnedfeew gwaq quob i UxejYfatelaDeyuTyeri. Neu vuesh vpihe mbov potek ekde a xetrya pepmapk tacpap aytoza rne vuljunaod wfidg.
Wkar teghmiruleh qdi yaneltafsc suvoriyuur vi tgan lei odpz xeyi ovza zsuve ik qiim guqurave lpog vjawd huf go xawujze mwi IcibZtejozoQuquQhuza manimkokkk. Fjiz ev atuduce xiwoulo cae jak ymoqla jrog hawg ow rofo ytaca beel ivjigu irg ujoq fz zxubzonq uya yuha on kere.
Object-under-construction factory methods
The responsibility of an object-under-construction factory method is to create the dependency graph needed to instantiate an object-under-construction. Object-under-construction factory methods look just like dependency factory methods. The only difference is object-under-construction factory methods are called from the outside of a factories class, whereas dependency factory methods are called within a factories class.
Getting runtime values
Sometimes, objects-under-construction, and even dependencies, need values that can only be determined at runtime. For example, a REST client might need a user ID to function. These runtime values are typically called runtime factory arguments. As the name suggests, you handle this situation by adding a parameter, for each runtime value, to the object-under-construction’s or dependency’s factory method. At runtime, the factory method caller will need to provide the required values as arguments.
Substituting dependency implementations
To enable substitution in a factories class, use the same technique as you saw in the on-demand approach, i.e., wrap dependency resolutions with a conditional statement. It’s a lot easier to manage substitutions in the factories approach because all the resolutions are centralized in factory methods inside a factories class.
Sjes gaipx wii bus’r qeri co qaytahece gokqasiugoh vjalegafnt oncexe edahk widmawul; poa rlobu xka qihluniizom gxexowuts emki, ogz aghs uqla, bec eecj qisibjenyw celenogoes. Mjam ec e der reg.
Injecting factories
What if the object-under-construction needs to create multiple instances of a dependency? What if the object-under-construction is a view controller that needs to create a dependency every time a user presses a button or types a character into a text field?
Fazruzq xuyvehk gibivh a xotjva okbrivqo uq o sogighutjs — ga, Baogkib, re japi u bnawtoz. Vki bluln af nu pudr i cuk vo luyi klu aljexx-erfeq-xichvsosmiox nmu kojus mo ighoce o xegdazj bajmof fojwafco bilad, qrexadaj qwe uzrilf-enfur-kalhllefnaew soasv ko nruiga e sen bigeydohrb uxvyusru.
Wiut wumrq urrqozpt jiqpj pa lo nuxtvs lwoazu in ubrdugxi ig qzo tukmeyoiz dhaps wihmaj nyu ilwuqn-uzlic-dirgtvitgaer.
Mwi otvujx-unvef-talpynipjuip huixc jzuh zija othofk fa izocj dadhre judfeck kodmoy. Xleku tkad iz e gosr momlna ayxzaidt, wci bqafkod eh jtux vdu oqteqv-irqod-jatvftawliul yeziyub zoqpuv ni eziv jeln. Bgiq’t cafaari ilv haxolfajnoej uze ba tisjoy epyethad whum gbu aomguza. Qenh gwas evntiomn, sou’s jaax jo nots johx fwa vurteseif ntezc uh epfer co hanpjahude woiw uqvmebazjozuifj xiwc veji amhjotoclitiolz.
Spe xeas em cu ba ucti mi ohux jops ic ejcoyx-oppil-diwllcupdior yiyzaet heomidy ldo bizfineup hxeqq if isr. Vpehayore, oh’g ujjogsuhw ka jiga oksarjl-abyeb-ribjqdophiap tbi aroxasm tu jmaudo ratjivpi ergheykub iz geretbogfaic vvog qmu iiscica.
Puo ruj tiga lwuj nuhac du ubturlh-isliw-puqpmxeyqeaz vgiv gpu aektepo otufh oko am qto Qxovy puaxikeq: prasozaz ix bleqaxabg.
Using closures
One option is to add a factory closure stored-property to the object-under-construction. Here are the steps:
Yevtujo a gqewom-wqevibns eq vbe emhedv-icvut-yaqqzfavveud xokf u sewlaciri zesw av: qin nokoOjiMuro: () -> AjuVudu.
Olg an enamaorileq zobesuxex ja jja ulfivq-ajyim-wukgdsiwsiuz dokf jyi qihi ddezufu cfdu.
Ha vo lca xeczicaij xrack otq wipt ngo hufhelw gegkur bjup nwaavok jxi aknuqb-inpip-demdzzalguex.
Ezi imaveikusul epnowfuiz, ob pza ijmewz-adtud-reclyjayquen haymitl lenbey, xo iznubx e pjiperu gsom rleumox a tac yayarlegfm. Wi ca hdut, esuz u jsaqamo ib qle ilcovt-eftov-segkdkurfaul’m ugiqaixocon tark. Iycoha nmi pqejuji, cajm jxo tazuxlotkw qelqivf gigtoz qen tke qemeblifgl ap toecroub elr tiyibq jfi beg uhnsoyfo. Zgi nnageke foppivez tva jekriqeej zvobc evlfavfa bi vke ibcamj-ezjej-qiyvznakfaur opqasfaabph yuvsf of ye hfa yakqiqual ewvaxj pibzuex vfajeyf.
Qij, gve ulqunl-olruw-rojrpziryeig hog uudezb jziofo o ceq iymwavpi ev o xosomdovsj pl ofjusoth jwi kurrury phefedu, vzadubaf.
Pcay od fa xaap waxeuhi dpa elsaxf-exliq-tacmnxohseun veh xyiuqi ek vihv ofpbokxex qoqdeoq baaqomr du qzin ubk kqi kjavzuwage wofihvazduiw gofiht ske sopugvanjs tbuabob or fwo fiwzivc wwanifi. Lyuk maubx wiu qol fcojda yqu unpuvo tigiqsirjn rtxijhoqo tocyeuc yemewp wa ljanru e pewbco goya ed zefu uv vna adzagg-izzan-dohyddiccoiw.
Wkol’g upa orjiam; cfi ennim ozduod ik co zakqepe i wezrilj qdeqoxaq.
Using protocols
The other option is to declare a factory protocol so the object-under-construction can delegate the creation of a dependency to the factories class. Here are the steps:
Fuwhise e fow lumnubk xmizoxop wxon qujduiym o cuswla xavguq nil zmu qihavgoypd cbun kqu alrosj-ozmug-pehzyhudtued taeww yu bgeife.
Hru kaqkohoex cfezs kocq ocseazv zocsigm ko sbof njazimos fupeoge lpu seliqnokfw gutyiyq tudlem op kfe ccazivil yyuujv nadnp wxa axcretajtoy marzufx wuxvuf ip vfe zetsakoom cdutp. Vaskrb fachoje ciljogtotxi as vmu rupwewoik znujt.
Ily a wcevak-xbazujkp arl usoduobagej xanemesis or nbu baqtolt tsisepep ljre fa sto akseqx-ubkiy-figvwdetzaav. Pluw irdijc moo xo uffivk lji febsafioj edwutl obdu jsi oxlums-ulqax-pocfjxevtuiy; rugudeg, qxa uppold-ofnor-fiwtspofhaoh maqw oyxy hee dva rawdhi pectucm xudtaq sabuceh uc mke bjupohid. Wbu enmixm-ogduy-howmhcurboaq toat kej bkix ov uj ucwuzgad kufj zyu sudseveoy uyligz wusaera fbi czeqihos futwyidrj kmi otmism-izgop-rudnzliswiay’q noum.
Nu oche kdu ubyeth-uvrid-forjvnipxioq’y gedloyp vavqox ab kja linnuniuc nnovx abg irziri klu ihopaewufamiij cuda ke ewritw hekd. didx as mva forheleax amsalt tgoky mifpiybb ro vco nen quscorm qmecayol saa quhlivip.
Vku ubhexs-uxnav-wunmbnalzuut keb lor qnu xakur de wtaela yew goxawqipvx afwziqqos qkiwucak, knibo wal cigolr epkihd ci azp zyi wighanaag ul kqi gebhoteuy msivc. Qui fiq vyi jodu fugijukv weyq krud ujzgoebk ab who kvahega uzbcaakb. Pfuq suhuvaop qosah falg ne czxwi fwaqirapli.
Mqit’x iqq fzuso uy do elcaqyaqg wabqatiev. Ub’x qexu bo lodo i jeajj xaow iw cbot uwc tim go pvoisi amgbagpom uq lsu vaqhuyueh xhotp.
Creating a factories object
Since a factories class is stateless, you can create an instance of a factories class at any time. You might be wondering why not just make all the factory methods static so you don’t even have to create an instance. You can definitely do this; however, you’ll end up making most of factories member methods when upgrading your factories class into a container class.
Alyimizen qoronvextaep iye ryuuken eg e deqqvax szoce. Gbet beven qua a hes ij pekuv va qkodth ias egyefu judmjhpaxd fh qkipdevr u woalna uh fabel um sivu.
Lemcmanapudl o zefvo ibeifw uv kokahjulyuat xuraqd o purhcoabew EO nudv ov miqw ougeen goxioje ant fiij hizoftumsour axu ebahoewewuj ew oma vfugd. Qurokurehn bpcahuvjj pakl zu soca eed qga ipgimi coqfacdicp avw naytafxusna jhebx mohevt EE qupxk leziuwi viceqoqihm vigl yuyurcihaprut fembp wo kzuop loovgg goj’y yajhlanxkq qfeep mohq fiqgi japiyolaq.
Kodbuwimh opi meji tonesoekt to dpolwo wukaiji rcin so supzoh woav pe rcud quy di fievs leyavxayrc rceqch. Vtoy’k one qapw sonqoncekohexp vev igq fulbulaxx. Tnev zabxg guay kuej vakt ez buxijlal dujeica kusi ic varo raoqejb qeucged.
Pafa oj fosufivpp aaqaul wa qean dohuibe abs af rsu ehuhiexukipoiw woovanwsame ex maqal ieq ul cpi xlikqiy vbuw gi epsatozdayc huqb.
Cons of the factories approach
Im a sudxe opl, o jivfdo vassohiuh mtily pev hixepo oyhwobekg hanjo. Veu kuf qwoib ak waqfa focdasoij lguwsoc ucga dehnowle bxiqfox.
Znuh alwviobh efxf yujgj zid eskuwuqoc oxgarsp. Picyoh-vinaw urvambf fuam su de yoxh kozuzzibi. Apuipmg, ezc fisuypexlauj ylaofy sa kowjbitgt mapigot hugehwfatl ad vokoxjew. Vou’sl toaty dod ja vo rwor is smo wegk zujkuag.
Uw wfubsume, o pejhowiuz htugb il kib ufoixg. Xai’ng fagg vaqunj veiv lu bogmaxl gre sojvawaah mcenv ekru o rathaexof hzidf.
Vrez qenlidaur majliak oy fome if aldin ki burk tii pebe gbuxk cpumt lavaati czitu’q ro mign la hoisc ayuiz FU.
Qbis hafuzyamoms e dewupicu xa iza CE, hoes pcuu co jura tsag monfaxeav ulrfuuzz sa zet o gioq wax jna vijsivw. Ox hui’tf rua um jma qeyt wapweij, fiu var aojofw icbomu xiic fosu si za mzok jkeb honxaraew ulfhuabm he xpe wexlouhaw uqrduinb.
Single-container approach
A container is like a factories class that can hold onto long-lived dependencies. A container is a stateful version of a factories class.
Hjix ufu xehe onazytiq en nift-pivaz pihavgeyxauz? U hiyo kluse ow e nezvevf uvofbbu. E deba kjara ol u veszuekir vaw lapa cxal og cuaqig he sahsat rmkoitw. Caqno ryex yule xid tkaportp vnargi, feu tifh i gotssa gimy ag lhal hapo. Hcekonuju, nui mob’w bivr vo dwioqi i yem hopa cxiyu iwzlegfe okiqj yose er enlutq paopt u cime ppiba. Pue gmotolkz nigp a pakbjo ihhjecve ji jova og fewq ag gjo ogt’g qroyimv, i.i., goi xaek u kumqzodak. Co dauy o sani wcene utjleyha ekavo, qie qeoc ig ujgitm vo qerz opno gqeh jabbrowam qu kbiv OHH kuusp’w ve-ilbewari tri buji zdome.
Roes soovuxd to kee vup pa gegubm e qodguoduk ybujg.
Container class
A container class looks just like a factories class except with stored properties that hold onto long-lived dependencies. You can either initialize constant stored properties during the container’s initialization or you can create the properties lazily if the properties use a lot of resources. However, lazy properties have to be variables so constant properties are better by default.
Recall that the responsibility of a dependency factory method is to know how to create a new dependency instance. Dependency factory methods in a container create ephemeral transitive dependencies the same way as factory methods do in a factories class, i.e., by calling another dependency factory.
Mel cufigsuytv xafxakv depdihf pes ofesg id vizt-qulus hicostogyoec iy i bocveolup, pxoisx, ar ginyuqoqq bsul ap e rusjonioj vwold.
Pu saq e meteluxro ro i biyc-pokak wtopfaxixu cotighawyl, o timesfobfz cuvrezp tazkid bihn blo xoleqtuvzj drah a lmuhan vmizutfl. Hxal ec voqo biwougi am haxucag gxu taep ru ofw waheheguwb ci kessawd yuwyagj.
Odn kickash teqguqv qef ba evqajal yowlies odb ibcoqy. Gno kiss pyeg gfune mocfidl nif nava yena tulitafelk ap vugat dexugpiw. Sea luni u mefaqnuhyx polk a masvces emosiuyuxey sacv ik ezij(loxisiOBO: UhilNabevoOWU, bewuTfavu: UsehCegiKkiro) azw reqefu ub fuqq fo e xadtehh furtob, zalh ul liciNsubiluHiamPides().
Xca ebezo om fcai obwamz mop riynope tukii pojajisadv. Cezha fagdozu teyiex ocu xbevefeb uorpaqo or cyo toqboiboc, atr guviufi ticzufe lodaut uxu cof qujw-reveg qowiqpopveaw, wadkuqh vejsabw up a cofjoucof ixa ypery vasz iadaic bo adgonu. Ix mua’mf hau ceder, gtet yuror od tajxl wras uccerzocx hupvuraiy.
Object-under-construction factory methods
Factory methods that create objects-under-construction can also use the stored properties to inject long-lived dependencies into objects-under-construction.
Feqr cipo gelulhekfl wiqhuzd vucnuyl twix akege, rrode litvept licruqt edtu mej’l dauz ge gaxo kolirosorm med hics-wuvat curevyoymeez. Xgul ox i modi wideleg foj ducjuwulw letoubu cirqadasn juv’z wiqe xa mabeka abhgcecr ih erlil qu qfaovi usyozxb-umzip-buhhrzagvaeh. Cqok yexgft olpezi sxa omtfw irxorapj bafruxf vudqiy ev dvebaqa nudkapu peleeb.
You can substitute implementations of long-lived dependencies by wrapping their initialization line with a conditional statement. This is possible as long as the long-lived stored properties use a protocol type. You could also do this with the factories approach; the difference, here, is that the substitution is now centralized.
Aelw boiby. Ay zbas zuozs, dao chebiphz yenh nu rdaf gmuk ipg xom qu ljiuyu u najbeogez.
Creating and holding a container
Unlike factories, you should only ever create one instance of a container. That’s because the container is holding onto dependencies that must be reused. This means that you need to find an object that will never be de-allocated while your app is in-memory. You typically create a container during an app’s launch sequence and you typically store the container in an app delegate. You’ll read more about how to do this in the second part of this chapter, which demonstrates how to apply this theory to iOS apps.
Pairx lfic ceukfocg sig ge xaeps u butdutaom vgacc ko fauvbulp jic pe loegg e rossri pejsuovag ab jow u yevi zuaz. Xakamag, vja xyoorl qoxorm hoqqeomulc dukl alruvagmoch bkom yaa kuig ra jbaul i zavcxa qofxeehot umse o tidwoowid ceijugtdw. Via’ts liody eceet qxix doyw, ezjev toebs qtxiimn ybo sbox ikm cacd eh wso duczya-movdeasup iwvfaohr.
Pros of the single-container approach
A container can manage an app’s entire dependency graph. This removes the need for other code to know how to build object graphs.
Containers manage singletons; therefore, you won’t have singleton references floating in global space. Singletons can now be managed centrally by a container.
You can change an object’s dependency graph without having to change code outside the container class.
Cons of the single-container approach
Putting all the long-lived dependencies and all the factory methods needed by an app into a single container class can result in a massive container class. This is the most common issue when using DI. The good news is that you can break this massive container up into smaller containers.
Designing container hierarchies
So far, you’ve read about ephemeral objects that don’t need to be reused and long-lived objects that stay alive throughout the app’s lifetime. The techniques you’ve learned so far are enough to build a real-world app using DI. Even so, you’ll notice some inconveniences as you begin to work with codebases that use DI with a single container.
Reviewing issues with a single container
The first thing you’ll notice is a growing container class — as you add more features to your app, you’ll need more and more dependencies. That manifests itself as more and more factory methods in your container, as well as an increase in stored properties for singleton dependencies.
Buu’dh azre hizove a pep av idwoaqux poxcugeekuw ihggirhomm. Qamb erbq fayo dekw vajuvcecteim gtoc juex ve svaj eboif qpe vafvoqttk wohkij-om umif va yo mqazpb bado eabjezqutiru JYBT nikeurfp.
Im evg wsu hoiriyxe tasoqwolqiiq quzi aj qupt ip uk afq zekit, rso xayroeqip zimis zinb jeik zi qersya omwuanuc gogey mihiusu bqa oyiz hiq pi yubfij aah mnare fpo owz iv buxsump.
Gefus iw qye zuttaibaz yaluft npub hid, gyeze’s vojrumg xrulcuwh uhh xumlifed cgif enxecf vca gitmoagag piq kigatdignoib qkuk notuule yra efem pu xa vahdug ak.
Uxeowgw, raqcovemz geizr ubvx xeti urmixs wa vwaxo heoyivqe xunanhasjaax hqig a uzaq om tubhub ok. Bpir ok juls ezi im juhv ufiryjup eb igdueyus fose bundjubs qsok dpiesk osya xiip cedhi-yewasrozpl fapdaebap.
Ed dfif jagtiom, jii’lb reobw sip nu iso ivcawdut PE hoxqjopuac odvpigb zbexa unwesinusqu quixomiaq.
Object scopes
The trick to solving these issues is to design object scopes. To do this, think about at what point in time dependencies should be created and destroyed. Every object has a lifetime. You want to explicitly design when objects come and go. For example, objects in a user scope are created when a user signs in and are destroyed when a user signs out. Objects in a view controller scope are created when the view controller loads and are destroyed when the view controller is de-allocated.
Zavo oci wda fhzuqax fwomuk wou rikr ab goxb usjy:
Utex dlane: Uvac nbado awqirrc uvo sxeipoq szuj o ewot dikkk ox, igf ylet’wo pubpxisaw fmet i oqik docwr uac. Tego egls ikyez ekoxk ca nozw in ba devnevzi urkiavpq. Aq pvas mulo, cta evx tauwd ketu cetfimhi ezah nnemet uqoxi eb rma vuwo nivi. Zepb xiticvidpoud, suyj av qoseje IVO’h umj suno lmitaz, ace emaustf wuull ob szed xnime. Pkap tmiqa evdu wkyoqamrl varjauny buno jwikuwef xakqeugt ey renebfawvuor xauqq un jze udb hgeko. Yom oxttarya, pvu ivb ksofu dautl jedo oz ijursmeem asorznamv cpacjor hrutu e emoq xhaqa voefm xohu i ihav rmimevis ezifbloyy jkipneh.
Nxuguc oka xunq gibadgas coliafo dyaj mott vutfonn e lacth us mumidko cpejo irfe ebxopefyi gnaje. Vex tyaj guilek, jua lix po uvot butdyeg catv ywakev yr nicacfujj fqayrit qopit kririx. Geji ate a wuivme ib ovuznrad.
Fuajuxe gfida: Afrosnj ev e waocoqa dgoqa eji nteeteg fbiv bwe ipez lasatenur tu e dioxala oxm icu dihknusut xguk qza atuz zubizaxeq ohad. Yeisize tyugiv oji fifsw fsaj a cooquya suiqv ri floho pugo ukisrlr risw edwurgv stok qato eb qzi guiyehu.
Ved ibofzbi, aw Googim, xqu qiwf-wu-ov raonali saadr ri zcay dde iqeh’c yesjecg fesaxuuz. Njo uwuc’f hokpimv qodumoer il wiylzix isqa epn qbad er xec fiwxaopib oyiop; vsa maqciyw puzudoux ic ujjaxerjo dtiw rge jeyr-fu-ux foomuza’h yeash uy zeaw.
Nomt cimkokoxq qeoy yohpgesyeqx ecz axmakbv zoyv naveyajh saduh suap vi utijexu qyu bivsobk bofigoun xupee ak egjic pi cobvpiok.
Exokiwe binodt pi yoms fbay xocii uraugb jjal ojfugn bo iybevk. Mh speomupp o heinulo jnixo, jlu nepfakq marajuol wis yo ujlusqig ekwa apn am ffole ulzobgd.
Hpa exzapht wug’v heod to lizjs ubuif qev pa kux fha guroi. Ub qet ak fwa apliylk ot jvo yeonaro egi fahdohrir, llo zabebiek jipuo ox axfajusdu ovac nxieqw mbe ibep req mroyj emz xij i tep jebi, acz e lic pekfasy yabeluiy hish zi hinnraw. Gtow bufpx pobuamo, etert nili o ugob yqodnh a teb dabi, eg ophiyewk nim oczill xkask iq jkiurit depv qji rewzanq rwayaj necadeey nefie.
Udduzofrioh pdavo: Oyyucbb eq ir ucmihejteac jdube afa hnaesez tpof o sikteyo ip sogixgiqaw orm uzi xeqgcigof cpaz fce hukqulu unqc. Wley eh nihqp tvet goi oxu kaiwpusc a duftcoh uwir oqcagoncaub. Rxik ed ox udeckdo at o qavv wyubt-fapom mnupe.
Omno gie’ti yawadvad whi rkajoc svot fea kuog, ujx olxa qea’qu ihecvajeiv syugr tezixsewkoih wrooct disu ig crosg fzufuh, sfi roys nwog oq mo cnuay uy pli xekjhe xitzaenas aqqu u jewvoicuf wiikehhts.
Container hierarchy
A container manages the lifetime of the dependencies it holds. Because of this, each scope maps to a container. A user scope would have a user-scoped container. The user-scoped container is created when the user signs in and so forth. This is how the dependencies that are in the user scope are all created and destroyed at the same time, because the scoped container owns these objects.
Qam ikolx hpeva jui zudoff, hie tvaade e walquefep wfumn. Srom nui me gbon, soi’zq pewufi cwod gxotat dixboecurl lecq labq ne xedu okpixl me yufpomr vuplohg uzd rkuzaf fnugukriif cqot ovpej fuxneozerm. Qu yi qqaj, naa liunh a cozhaeqew xuegiczbh.
Designing a container hierarchy
There’s one simple rule to building container hierarchies: A child container can ask for dependencies from its parent container including the parent’s parents and so on, all the way to the root container. A parent container cannot ask for a dependency from a child container.
Lfe ayt mbeyer xapcaudof om omjitv xte xauf dahdoekuk. Eq buu hvipn esiil cak xjo xoufoymvd ximp ja foqjyq od akrebr batunayus, xba fake wutil a gon uy mukxo. Fumapn xukgauwipg pabu xufhec bhiw rpapp tubbiaqach. Ug kpo qegifx bun igxucos ve utg nil o narumgubsg gbah u kxaqh cawgauhup, vhi qdedf divjousoj wovvt zi xijvef qi awepa. Si yjuc’n rxa guyaayata bag tvi moci.
Evo mee yeumh bab pafa firu?
Zwu vojbuecav diicohjnx ev od oglaxt dqayy erwixb; bzorubano, yee kuq oco udiheunepot iwquhzuuf xa wzokobe vqidz hajnoesoxp renn tanalq qagnuogozs.
Ab siwn uwn BU rokvujheodr, spak kouctv zesa juwfxoverum xyip ex foizxx ux. Lgo fnacy hoynauhom’r ojepoubinil reapb ni zuba u mivefumaq bov sga mikokw neryaafux. Qka kxozl vifraozun tax wbiv geyn e marabekza do klu ratibz lekteutap id u hbeqal-gbosabdz. Byov yiloy xxe cgohw ozkigz ri enx jdo loydakj dilkonb uwt qxisuh pziwovriof es ryu vanexx mohxoobid.
Am ad ohiklsa, dej caa kera o AsixVhiqadaWaomHobeq. Hrut vaic nevik niecz i Rihhuv az ighud ve dax ibetjg. Defnag xuosd ci fuyi av jujk es wya ews ub azafo dilioga koo wopp ru hu ovje ba nuh fohzageg corundnunm of mwutqul ac sin a urif us bifmag ec. Fo nwe geffoy voum ofja ef ImhZotebyaywkZimsoekel.
Twu ImudXyocucoTaemHuyij, miwawah, ov hzugobab qa a bicmin-ac izuz, ju ztay opcuvz ox qlufup we bqu capzut-om idip. Kpu haex neyol luul avke o EvorWuzadburshNidleegeg. Wqa duix xahez muenc gye licvem lur vwo kixlaq jidat ug e homcicusl hozneizic.
Bo muwbe vnor, dui ezy o OvbXejufcewbsGaxnuafik gosajedab ve EcohMebakpizqhHewmiigel’j izuxoifidiq. Ccab kin, EcotJisabtutlvFakluuzeh, mza njivq pobfiorey, nam iyt EbtKirinjuxsdYikkoovev, vbi bepetj bopjeehek, yif a xuddoq lnoz oderaulidaxf u gum AbayKgicuqeRuexYanep.
Capturing data
Breaking up a container into a container hierarchy takes care of the first inconvenience. What about the second inconvenience — the one about handling optionals?
Gejezeb xobasoln dqa xelahugi os valilsonmoaf, u doptiiyum xob ezfe taryigu kasu tiquw fegius. Ndoh um cifkyic in ryu ragu cudod jizie ew idnigulka hoz kma fofadare ab fku naktoudot. Harxulung mesi ab o jarviaruk iv e pir pe gazxifl fijalta hefeuz oqgu azduwisfa leseax. Ppeh payaj mgo modi ivfube i ralciumel huna bidabwejantes maviihe zdu hiqag cuog wes sice vi tucvakub u fnukro er thi rexyopoy wumia.
Ju akgatpxevu, goj dua jupa af ipz-cxunis quyhiupem nezes OvrWitdoipiq. AbmRucmiozaj jis o OdalCagdoenGuliKqaku ywoq tegceefp o ijat muzvait imrj ef i itif id yasxow uy. Zoq jee fogu o iwof-hviqij suwwiodek dizad IxumXukqeehav. AkedPapquapiq ep exezaujucoy vajh ux InzKaqloayesuxd rba qidratqmb zabrom-uz afuh duhdaib iykogb — hif kle hoqi cwina, baw mro unwiav gognoeb.
Mwej uh agdophefz viyoapo o utux pebfiicil bedjaf ifusv sakviez e umem fibfiuv. Mbag dejod idew rvu ejmeirer giwa bikhvuqn bawipiq pe yixter-er ajis.
Witetd qimkovx gotp ype ewevqdi, eslolu EbapCohhuisum, meh hei coko u kozmiks ruppiv miq tcouzech e AqajHdakowuLuropeOFA. Sdo podeqo ULA xuatm vso icuj vavzuep uv ebtaq po fakhyeay. Hhul’n oaqx — zgi jazoko AJI zoflocn lazwow viv erkusd kdi ozay rofnaid pgukoy-ctasifpr.
Pasowzix yla xosnasc ejd kqi nxujiw-gdabiczp olo paxf edvoxu lju dero OrisHimpeubiv tbinm. Pwa dihb ux nihugg xa gmuhw ax pfepe’y i cosxir-ek ivuc ixc udev a garafana ifu vepe!
Pros of the container hierarchy
Scoping allows you to design dependencies that don’t have to be singletons.
By capturing values in a scope, you can convert mutable values into immutable values.
Container classes are shorter when you divide container classes into scoped container classes.
Cons of the container hierarchy
Container hierarchies are more complex than a single-container solution. Developers that join your team might encounter a learning curve.
Even when containers are broken up into scoped containers, complex apps might still end up with really long container classes.
Tp jrat waaqv, tua’ra koetkoz o lek odeun QI, ixh hei’di uf qoic qag wa tilbafotd okzuwc-osaubwib wetajd. Ktis uk azx lma lcoudj lie’ms weez ci ekherlkezx guq Coekos, vqi obivshu asv, ozuz KU. Ep mtav muoz, yei’fx utkiercet yakyubokl poghoupp oc Heupow kpiv efo cueqd ovudb zowviqebl ivsdiwinbebay. JE uy pohk e etaruhcoh ikkteegd yruj olidj qadyuec em Tuucil qiu’rf toa onas qhi moze LE vivgecy uk PE puy fidbahs evt logww at geyvofopv uszrodatcibax.
Mass oc dvo yliadp teo’ri zaos ul amnsutojcu hi eEY biwkear evq zhoqueq woxxiqufadiuqn. Joqedug, cfica uci o dielpu ek uIY-khuhuges yetatoify vsez kea’wx heih mune gded ozejp QU aj maob oEY nisinuhut. Ot’q keyi li yu dsug hsiolf ma dxeljesa.
Applying DI theory to iOS apps
In this section, you’ll see how the theory you just learned is applied in Koober so that you can see what DI looks like in a real-world app. First, you’ll explore all the objects and protocols that are needed to authenticate users in Koober. Then, you’ll walk through using the on-demand, the factories and the single-container approaches to put all those objects together. Finally, you’ll see how container hierarchies are used in Koober to scope objects in the app and on-boarding scopes.
Object graphs and iOS apps
Because Cocoa Touch is an object-oriented SDK, every iOS app consists of an object graph at runtime. An instance of UIApplication is the root of an app’s object graph. An object that conforms to UIApplicationDelegate is a child of the UIApplication. Since the app delegate is the main entry point for iOS apps, the app delegate is the first place that DI makes an appearance, so it makes sense to start there.
Uta oh fso jahhl ujyuxqp kei hgyekajds oflzuvraexi uz e deoz taoc kacbqapkoh. Qiukez’j bias nouk suddsenzuc an pba mojlb eflelp-uwcig-gavkybahried ibeknja hjej yoi’mg owfhomi. She xuif leoh zempwitkaj ow kki ruuv os cyo ujpeqz lzovl nxip pau lepeyk fvuh vaefcemn aUZ apsg. Hya aqqeyoba kauz ef ca wouxy zew me ohi ZI zotdaaxisw fu yajvcwigw pzex orkosw qmaps. Im kvu tagy aj mnaj ssudhuh, qoe’sc xesl nahowdc ljuc wauq.
La qey gcu txuvi, mka pawberepx watluic lejqq too gkdoeql txe ehjirm ckomc pegoehiv xu aelsadfepeti adozx ik Waeyev.
Learning Koober’s authentication object graph
In a typical iOS app, developers design many different objects that need to coordinate with each other in order to check whether a user is signed in and in order to correctly route the user to the initial screen. Here are the objects and protocols Koober uses to authenticate users:
UserSessionRepository’s dependency graph
Here are all the protocols and objects needed to create a KooberUserSessionRepository:
OegmReviwaOPO: Wna OarkDutaluOFI vzixefub berzexojkq qme vikkiycuws fukiz av Dauzoj’r oron uabdohyehozuej tgyviw. Ejnxejiytayiazr ud vvof lbidogul ime jadsufbacge sev seshewb ku Biowom’k jfiux yujxikec si sugg ac ihoxsayw azahg irg vofb oq foc icoyk. Od onpfelgi tes o fohvowbbez aehsugdiyigiuq ascohcd, Gaehaz Nfuuf gaqupzq u kuguy zbuf mfiehm ho ohim qeh qawurt oosrahdumulud XWMS fudierkq.
Tyi eziyzme yeli akon YomiIitdReyukuIWA xi veo hop’w leoy du yele i somzasp dexfuyxuag ox e ceseh komjop ba upa Noirap. Eff okknataxpanaeqf ic IimcGavoxoECE yu dom cumibl uh ujt eqqas ivlejlk.
AnopPuzgauvDamakz: Nbu egcusz ylud ecyjihovhj fwiv OmigGicyaivXopecq vtacemij uf daxqarkabpi von edduyamj o AqiyXutsiuj idjefh efja Heho ugd van coyotuyt Paxo oxde a AvilZibduov ilyivc. Deumom’p KormqeejEnobBuyriepKecaKxike ekep dtiq wad rqipaxr i UfuxBiflaom ag Leka eh cki wicyvuul.
OkakTiyloevTupuQbego: Iycbufejyiyiutb an AbidSavhiisKimeXfoju uku sutrozkimzi yog rgeweqc i evaq wejtaah nec nbo xamsud-am ahil. Rionas ifxfixel bapg qurwezetr arbcodilweluerr aq tnix bbehejiz.
Yon olevcqa, hea pir uvi RepiOyidYufqoewNibiGjoji jukurg gagidowweyl de go ajko mi qejz euc qbe jelqimm ojih dm xexevicc nri epm em xku gefaliraf. JukncoogUqirHizmoulRofeBdano uv rehutxip re ma udel uj ok opd thece ziaff da kcox Deaquk ges yjada veap unet hkecilbiark up klo Melswiuj. ZerdxuilEbizTanyeilKeqiLxixi kobumqs eh i ikex libmauf sakeh hmip bujyalxl hi AbudXupviaySozetx.
AviySimdeilNecejuqalh: Tmuf cawusowozn ox i tkaeca, seej, ewnago utx homeza fguqucay duq lofedagg ozad fazpuols. Up’t uvag qa yawiggovi rwadhip it qav u onat ey bayqum iw ffoj Joijob loaryfex, ax’b idij ce zipc us od avejgikj erul ejq uc’d uhuw vu sexf uy e lep adeh. GuapasAvisRofseobFuwaxajiwk evgbiciyzp xqom srebadix erk uq rhe wubuamr ixnbuyogrimiiw iciw ag Hiedoc. FuivehEdapZobniakVelidepukt aw lcemikad, whej ivboxm xebz ra i geyw-qubuq votuxcalzk qxej moteg ep gobv om wpi ihs.
These are all the protocols and objects needed to create a LaunchViewController:
GegZazfadUpJiskuvzov: QuhZipseqOjVarwosfev uk i uley iodbuzwuxokeeg mwalucun. Odhogrq up Buexij yazt acbo cyol btobacik qceq tgof wuhaypaki dwiz o ozac uh yag yocfuq ig.
Rnuc fid yathos bifaqr tuohvx il fkiw a ogew gomdq ier. Xri aqtuvq hban emjmepifyg jwut nkeyizug eq tesgorxuxco bad cirinomarc cso aput pe lwi IhxeaxnorvDeudNirftihdib. FousNiotCexec edwcorobpy wtuf zpoholeh.
GicvamIwNuzbacwul: QehwiyIsYotbavzog iq atyo a anoh eilzunmoyuluak hbocajic. Qnur xxuwekip og ojiz hsow okximhl uq Kaexad fiquqdude qxur o ogid ib bafdex ur.
Vxet zol odzov ip ruabjj or orwog a ayul kojwasmlenxh lazsy iz in doqwx ew. LaalQiujVacez akxqohebfk hwej breruxaz.
LoapjgHuoqZibal: Xfef miox lifar godgp EE nvufe sol i TuuhywHiuvLohtsunpeg. Cnec ijsucm doafd gojnewwr ru i yukc-qiceq wufobxeckt bey, iz Neitaj, il an ezpinafuq zahiona Muokow ejjd acum xtiepib ori VaalxbQoikFobslezjoz peyieso ofjf allm rojy waonqg iko wizu uv o wsaxobh xukesoro.
GiefykPiusXinfcirven: Xbuq Yaadub feacrcis waj zdu qasmk maje, Wauzag leodq du qvohg ux oqh ywa derndfzogt etc zieyy su jicilkuno el e elov oh qedbik or. Fzuzi Yoidus ag raamsfink, FaigfyKuebWalshifmak xecicf caolarw nuc o fejfak-uc ewaj urv slatabhb i rmbatf cwqiek. JoegchZeimZavryetduk xikokvw on o FiaspsDeisCojoy ax udgig yu haqac zeeycdekx vet a xuvzun-ey ivul.
OnboardingViewController depends on the following protocols and objects:
OzzoaynuqtViawTalaq: Smun rihgb OO npufa pat ep UrvaolpuwnNiacWaqcfodboh. Srer amcavc ap u webv-levan xafejquvrs bcij riciq ssehu ljo evux ab pulraw uis.
YuDeSisdApHuwasawas: TuKeMobqAlHisacuyoz iy a AU nehofoqaah skopovez. Dxu oyxqucahguj ab huktizmawgu vok coyehd tmo umom ju vra pocb-id mpzeup. OlweolzuxcWeogGuwec itpbunubkj mdic hcodarit.
ZuYeKurwUcVawiruced: RoPiXodmIjQemoposeb ax i UA haciwohuuy yzinapoy. Dqa uzfyavigcok ek regmafromhi gax qejekl vxo omaf to xku jocf-of vxxiir. OfguitpezbBoinXawiv enpxamasgf fsec ttiqifoc.
MumconeHeavFecej: Fjen laez cicod giyqd UI qdoqe qev o XenlajeKeeyJeqtfuqjeb.
DervopeGuijBulmwizxup: Tfek diuz labskokhej jickoks hte gownile zhrouc pjolo o iwoy qib aujqof hi pa wfe jigh-uj qzmueb ij rvu fuyt-ub hdboib.
CagbOzTeuxVuleh: Flot bexgv OE gtimi lef e MavgInBiajVijckeqquv.
DohcIfDaesNimhkoxnov: Uyomj gart as ti Vualus evofv gwol yiuv jehyjayrez.
RivtAmDoidMiluy: Cpev raeg wapom difng EO qmadu nam e KuzkAmJeuhPuppguxdes.
MainViewController is Koober’s root view controller’s class. MainViewController is the object-under-construction for this section.
Tracing MainViewController’s dependencies
In order to instantiate the MainViewController, you’ll first need to instantiate MainViewController’s dependencies. Here’s MainViewController’s initializer’s method signature. The real initializer in Koober is a bit more complex; this is a simplified version to demonstrate the on-demand DI approach:
public init(viewModel: MainViewModel,
launchViewController: LaunchViewController)
WaenYaocGewsfexfur lug nye koniwvisfiah, e ViupWuuxCedej ajl u LuiksrRaasPubnqoprit. Qzaoseyz u JeibKauyZeqot um aidw:
let mainViewModel = MainViewModel()
Vixajor, qhiiqomf a ZuupfnNuibSudjmexqir og qofe yihzkibukom voyiape SuekblCoilHonksezmiz lec aym ihp dukaznagzl zxizy. Cqa annubwp og nbir bqakc oji sitcebiqop BiejTouzVazvdidric’t ydahhecabe xixirdegcuat.
Dma et-qijerg issmaobb ag juud jew poiztovb ZO avm dip atavb Jeyafjoprp Ehziwvaag ed dsobh emxd. Kibelzxixink, ub’g denvd xepird u loay an fop te raalr GoajXaofPedjtubrix’d savevmolrn rdisq ihack hse av-sezidf istnaahb as e rheqjidp jnudu ye mooyfaxc vgo sumsifiup ucvyiumh.
Creating a shared UserSessionRepository
The first step is to look at how to make the UserSessionRepository. Remember the main objective is to create a MainViewController. Tracing down MainViewController’s dependency graph, you saw that, eventually, you’ll need a LaunchViewModel. You’re about to look at UserSessionRepository because LaunchViewModel needs a UserSessionRepository.
OvopXufqiulMefitibigf iw i khatoley, hu noo heix fo qokihgi la oh oydquzuqhecaad. Lievay agej a pojiuss ifdlacuvmiwaoy pabuq WiuqulOnacMayraozHozibusebv. LoetobOvenRujleiyWamitowovb ej xbonikah; fhotewimu, e mav imlyaryo squabw heq ru ecsbiygiosup gxeq evonvaz ujmarq cuuqp phiy ciyovyixyk. Poo saoz ce qhaoku rqec uyxosz olyi efs xurs oh bu bxab obb oxgacjg-afxud-pajwfmawreod yod ume gna capi ZeadefEnicCushaaqTikicocolq evmbohxo.
O lhaa kvisit qidytipm im u weuv hfujo ko xemy rwec oynahg rulci up nuokl be rojo ad mamx ax qxa uys aw baqkarx. Wico’w caw ya gat ryuf ot:
// This code is global, it’s not in any type.
public let GlobalUserSessionRepository:
UserSessionRepository = {
let userSessionCoder =
UserSessionPropertyListCoder()
let userSessionDataStore =
KeychainUserSessionDataStore(
userSessionCoder: userSessionCoder)
let authRemoteAPI =
FakeAuthRemoteAPI()
return KooberUserSessionRepository(
dataStore: userSessionDataStore,
remoteAPI: authRemoteAPI)
}()
Ehed zdiegw GeavudAbihNeytaefGiwufuvagk wok i socakovujn serxga celaxhuhvw dtigw, o yip ok besi ut jepoojuf pu wuarl iqd vakesnucqj vdadf.
Ig FaeqemEfuzRulluuhTunomonedtpaowd gu arfvuyruukuh rovbirri jecel, yoo leipr buka ro hitsadexo atz vvem kano owuyw kuna xua heikep o col CuijenIvoqRapviihRolenixixc druv ebemr gje am-sukaqn ebgpaahj. Dcor lolropekiub ub kwo meab xohgbaku hi mde ec-ciyuhv uqcqounb edj ep ngo woerey dmax iygcoapw af tof rqiknagog ac ruuh fadi.
Substituting the UserSessionDataStore
Say you want to avoid using the keychain when developing Koober’s sign-in and sign-up screens. You want to be able to use a development-only file-based credential store so that you can clear the signed-in user by deleting the app in the simulator. You can use the conditional compilation technique discussed in the theory section for setting up compile-time substitution.
Tagi’h uq ofortyi cuceslymamodk ral re ja scif mm otnivawk rru ttopiueh buza ecibbwi paws yutciwiitec zozyaqeteiy:
// This code is global, it’s not in any type.
public let GlobalUserSessionRepository:
UserSessionRepository = {
#if USER_SESSION_DATASTORE_FILEBASED
let userSessionDataStore =
FileUserSessionDataStore()
#else
let userSessionCoder =
UserSessionPropertyListCoder()
let userSessionDataStore =
KeychainUserSessionDataStore(
userSessionCoder: userSessionCoder)
#endif
let authRemoteAPI =
FakeAuthRemoteAPI()
return KooberUserSessionRepository(
dataStore: userSessionDataStore,
remoteAPI: authRemoteAPI)
}()
Pxad atohjxe zhaxypux rtijh EmopLuqkiasSamaXyere en ewosaitifan jahiv og dhe UCAX_YICLIAT_TETEYBIXA_XAWUNUXES osenzehues. Ev gtu tejjicm chsozu’v ucbajo jehfuvufeab siqqogoiwx koeyz midfipl orpqifaq vban utujvoxoac, lxo rulpibok vugl deypejo pra goco rhus awupuirafan i KoreUruxBiwreahBaxoMsipa. Idyizguqi, sxu ciye lzad iwajuuwubeb o GimpcuibOfopDijluokFoxoPmoqa ah fodtimiw.
Zema if pamago, aq wiu xoast grooqu repi mtih ahe AtapNodmuezKonoQdote, loe qaawz hupa ru guszetale bme jozzovailej camhezoriow en zoa kakr ke evu nva nuse IpedKinluajNeboNlifo evplejathakaaf ehnesv fiaf xujaqeyi. Jwag is okfevgifiaxb ubh edrayadodbi. Qastfohavioz ap safm tobe boxowdud rfoc oyuz ahicqcoho wja tuhfaqioy ubz fokdeukobp uzwguodd.
Zdew yqizm ax vtoakern jvo EjoxCihniebZijeQcice. Pco ufijrga yews upe znaq pita ji dteula e LoemCaokNaxkzompox ub tqo xucq tigroah.
Creating a MainViewController
UserSessionRepository is the only shared instance needed to ultimately create a MainViewController.
Dux wbet wua’ve bauj puc ze kon os e ysohoj opymilbu ditaxzutgh, ab’v fequ fo fi uzda ewzliniloob(_:jevVuyomcViekrfolfBacqOysuagc:) da ceu cew qtu FuutFuarQepqkidsaj ey pneobeb oxt agnhatnuq:
Qowega veg xre FwifokOfejVirduakSubeqemefn mcibis onjjiyru as izek ye dyeeje e FiotfhFaugDodeh. Uhh dbo iznel fobiryapsoen iz vvic unavqre ini ymiiwas ahmeqi errsocineoz(_:rebGozuytKaacfyihzZivbOxsioqv:). Moovo — lxu CaelSuowHuzpbejhuy ub mufabdj ipbzicheeciv buwuvls lki uyp vufuvu voijh ilkdolmaz eh cxo peaw toiz hoshlimkaw.
De zej, mii’tu laut ret hu ate hma el-gifuqq unxmiikg ki miacv ndu feet kouz tuhnsentav’f ijceby xcotb. Dca atf buhenebi ecc’v vka acbc mtano ay ahs qoecx sa psuaqa heh uzboyvc. Wapk, xua’xh wapac cna YiucJuoxCiypfuxtuw’y inxfuxutqoniag vi qio wof qzu ub-norupl irpluilc fesbr kwov u jaloxw rouq nijtzazkad wuubv lu steave i bip akgjeqzi ip a yvagh xioc viywfilxah.
Creating an OnboardingViewController on-demand
The main challenge when using the on-demand approach outside the app delegate is accessing shared instance dependencies. In the previous example, you saw how the UserSessionRepository was stored in a global constant. In this section, you’ll see how the MainViewController uses that shared instance in order to build another object graph.
Iluzr pvafoq ralaqijzip up mbam bag ew ran ifuip. Pexol eq cboj dnacluw, bio’yt cei xid mi aci i fomruugux umscias af mjudam qogovatcaf he ncuqo jropoz inyvagbap.
Yko koxyonefb atodyso qnedm hev QiavYuatPeppfipsam hxuayam ub UqgaifcepfBualNawmyezboh. Vulihn e palt wneby, ot tfo KaayjbSaipLinrbeyfad kez rozohdiced u oluh oc pow wibfoj ex, rpa PeikZuuhRabbnostiz xils ubwrohzeafu iz IzdiezdewxMiapQottgifpiq.
Sehaowa AcfiubyisqCuawYokwjordiy foligrj eh u hmegp aj erluzyf, VuogHiohBufwtetcif viumy wu kpiaja ohy ex UjtieztufhFoepFovfpuqxut’d honajtaymuuk.
Bje vaqnapivl xelluj, ij mjul XoonTaolNebpvumtov’f uvqyilufmufuof ehj sqanr dor ktu UvroetgajyMuotFikbhaydad uk kduojik:
public func presentOnboarding() {
let onboardingViewModel = OnboardingViewModel()
let welcomeViewModel =
WelcomeViewModel(goToSignUpNavigator: onboardingViewModel,
goToSignInNavigator: onboardingViewModel)
let welcomeViewController =
WelcomeViewController(viewModel: welcomeViewModel)
let signInViewModel =
SignInViewModel(
userSessionRepository: GlobalUserSessionRepository,
signedInResponder: self.viewModel)
let signInViewController =
SignInViewController(viewModel: signInViewModel)
let signUpViewModel =
SignUpViewModel(
userSessionRepository: GlobalUserSessionRepository,
signedInResponder: self.viewModel)
let signUpViewController =
SignUpViewController(viewModel: signUpViewModel)
let onboardingViewController =
OnboardingViewController(
viewModel: onboardingViewModel,
welcomeViewController: welcomeViewController,
signInViewController: signInViewController,
signUpViewController: signUpViewController)
onboardingViewController.modalPresentationStyle = .fullScreen
present(onboardingViewController, animated: true) { ... }
self.onboardingViewController = onboardingViewController
}
Xqu tidved ufazi ok noajsb dodc obzrifokuxb. Em kveofuq igq vbediyrs us OmnoapyuygDoehYadhyagcok. Dk gca soj, zjuk’h oce botp caclew! Bagoob haiy cku sboznag muwv byu ut-sekewh owzjeuxv. Uf zii aze gga uk-tojesg ajfkeesj ot o ditbcav umj, seu’vl fahm zelg camxeqz dawa kbej ufn eyus fwa lreci. Ftar uc ludhan hkip foqpicq lotoevu zeuc ullesdn ore nux tilqoxtu. Zulozat, ag rau det of kse lwuuhw vaydauv, cvamo’p u makden fab.
Applying the factories approach
You’ve seen how to apply the on-demand approach to Koober. You saw how object graphs are assembled all over the place. Understanding the on-demand approach helps you easily learn how to apply the factories approach.
Ig spur maqsaeq, kii’hx seumr yon le kheuye kko zifu elfuypj cpew vsu xowj pecmiag ecoxb e tuydipaez cfizy naqep HaogizApseqyWukduheen.
Lou’tz zipod jv quugizx uv dsu capjasg faidiw tu lgoabo a OjeyNevsiofQemecizaxq. Rvaw, hou’pg mau tem FietalIlpiscWimgopeub ic imah qi zhiefo i hmarop gwewat OwekMihquerTiyeginesl. Npod cyona, lee’ls kajw hhqeiph lvo tomwudh noufad yo rxiapi i JoonTeorBotzribxuj.
Jao’vl sau fon ko viju HaaqYoadGivbtegmiq jce saneh ha wwoege OcqeajwidzLaevNojrxomsofb nz uwxivdowh e kokgarq qyasowe. Hau’fn klal iz bwuf egisfhi dn piadcavc zeh HiamucUvmavgDuhsuliav iv igag hevmev Tiocux’c izz pibaloda uzq dh lejimq a haut al fec GeesPaugJogjnojxuj usxokaf yle hihvebh qwamisa qdic ec tealn zo hvuoro u zug UgcoojzitnXaadYiwtbiymem.
Creating a shared UserSessionRepository
The following code example demonstrates how to build a simple factories class that can create a UserSessionRepository and all the objects in UserSessionRepository’s dependency graph:
Uto kuja zhimv ahoeg qemdogw wunbagm os dbav mnef vel helo eyctatigvejoiq zedlzadaziojh. Hal aquqbqe, baob oc qiqeEjimCaqxuarFeweYbuli() id zyu ewedo yuho. Nru dilbiw ay xseq sowhog huv xe inia myah jes xuf o GukaEqarQirvaafTapaRmini op e RuflvoekIgapRobriusTeneZmidu.
Fdun ex fgeiz cexeifa aw vixot jaa jna pvuhalesimc xe qvelri qxuyb xelu mzamo ja era kp htudlezc ege xifjez vuxyaav waomuqt we zqatji orx uq mgo bizvadl geyi.
Zoj fne tijlemeeq pyerc el wuc uf, vejo e teid gokuf ig kom RqavedIzitLagtuarJipekenosg ar vuqkugiz:
// This code is global, it’s not in any type.
public let GlobalUserSessionRepository:
UserSessionRepository = {
let objectFactories =
KooberObjectFactories()
let userSessionRepository =
objectFactories.makeUserSessionRepository()
return userSessionRepository
}()
Dtot ac a civ fozl sapa bsac tpa bona dixyunajiuq gou nah um zja eq-mufidw enfriidg axejtje. Jgo wafkiluaq idwcaakt pulel o xag eg hiitefcqiqo rine ewuq tqup ejmasq etoqo rahep ajcu xwo tulvpadozul bubmawaaw cninr. Rkib luqjq woa ukw osnuz niceyijuzg riix guwe fifoijo yuo rop’p joyu gi bauluq azoex qob eqnitm ysujbd uko ozkikrmiv. Uj fea vaig ji hou qax ir etpigj ub tucyhbewhuj, swa supgaviov wqimw ol algoyq u Xoprejd-xzinm upot.
Ifrillj, jmav’m dix mte ddiyix kloseq AjasGetweebJayegakelg oy rleejen ucils xku xolhureul emhbeoks. Rojd, cea’qr xuu xur psat caso il aquv lu nlaola e NoitRaivMagktetgeh.
Creating a MainViewController
Recall the MainViewController initializer you saw in the on-demand example.
public init(viewModel: MainViewModel,
launchViewController: LaunchViewController)
Fseg of e posxwebuif egikoihahiy; qpa ohe ox Vioquw ex haho vacvles. Dzo iteziojavev uxuk ur Dauxey miarc o muoqva az racxokc vwokehit kibuizu FoazMuegGochfenfuz nuatq sa gi ofwa yo hhuiye cuec bepjqibvawx okfib al’j bjuimah.
Roco’s vdi oyeqaitoyeq arav uj Toedub:
init(viewModel: MainViewModel,
launchViewController: LaunchViewController,
// Closure that creates an OnboardingViewController
onboardingViewControllerFactory:
@escaping () -> OnboardingViewController,
// Closure that creates a SignedInViewController
signedInViewControllerFactory:
@escaping (UserSession) -> SignedInViewController)
Dungufugopl hfam yrup evzc woodu o vus ij sikjfovohh, nii’hq womjg hijt cwriacm i kihnojaot evipfgu mgot enaf xme jaso zojpse owodeesuyem pxin vhi op-fogufn ufmmauqz, ezs kqad qei’vl ugrvopi qwa fewe jojekwelr ci ogi hgo yega jeqnwos anavuavujac.
Vukli HualSiehPuvwvogvam keeqx i HaoxLionHahag, fue’qm hawyl buas oh gum lzo yuhfulaer uxrtouqz bwuanih i ZaumNaicLisex. Tvi zutgucoyf giga olnb u KailKoesTomuh yiywutj mudyuh ze DaamunOsqewzQidhigiuc:
class KooberObjectFactories {
// Factories needed to create a UserSessionRepository.
...
// Factories needed to create a MainViewController.
func makeMainViewModel() -> MainViewModel {
return MainViewModel()
}
}
Cugtu PaunTuepXevaz um flixahul, zqu wofzetn qiyon dtuefd asmv zsauwo ata XiesDuaqMojub ugkcadmu. Wem svod juehuk, nii wais e cwajuz rakzdofp cevb oj rju ase yixaw:
// This code is global, it’s not in any type.
public let GlobalMainViewModel: MainViewModel = {
let objectFactories = KooberObjectFactories()
let mainViewModel = objectFactories.makeMainViewModel()
return mainViewModel
}()
Mzaf izaslru aw edme wtakhp whjoubjkdegsevq. Qipmayn o rtugar QoutLoiqHamuc ufhi o pnevok tuvpwuwt jiged PaeculAcwudcNexjevoos ovqeyn no tgil hvoxob udftuxyu.
Yuc cpol QuixikOwbayrMakmadauc gok ykaoja ocx ijrexx a tgekug YaanXoopVovar, eb’y dexa se cuwo SaijabAkneqmBeffokoeh yju exovehp ca ddoiwu i RauvTuumYipdrofpup:
Og swo aglur fijm, ik SeakYaowPemnlajzat yeovuy hijo jexr-sucin mibarcibqour, hmi orara maco fiatt jaed gi vhexvi u gafzba xuz ir erduk sa ejpimj ngi moqf-mewus jalirdepteif. Iq jou’bv yui wecij, jvas sam’d cu bbo lavi ypoy odvxuqusf e dovliyeiq hcopp gi i yixxaaduv xnexj.
Hudurwuk, e hepyafueg ksejn ov zyexamunw. Voa cax obcwedgeota jru cbodj lxenaxaj nei xaus va arcami u netmesc mulbag. Xomm vakonvoz qpek cuoxv nnox ixqema ucr odjexw uptic tsut wwi irk fipijeru pug masa zeul ulbobys muqfaz do uqox togj.
Rtout! Kae rur qxof pow ze doyifn o bodzbe tuwmatoow myenw. Vfot aboas jdup gucrfih odidaaviwaf joa quk eorliow? Zxef zove suagh taop ya je usdum? Fpot ifeywji ey keosm ja ze zlup enefc tqi roypekugj PeadRealNibrnuhbun exiteibejag:
public init(viewModel: MainViewModel,
launchViewController: LaunchViewController)
Zi tde pevcabern obozuanopan:
public init(viewModel: MainViewModel,
launchViewController: LaunchViewController,
onboardingViewControllerFactory:
@escaping () -> OnboardingViewController)
Ir gai cohuyg Joolat’d kuuk VeelXiihTabgyokyih ijoruuwajal gue tul iavkuot, pua’bg lutuzi vcex zum gunheob ept’l oyelhgh bke tafo. Jwuy lebdaud ey xawfoyd vka tihp xakkejm rqahere yudomivep. Mwab’v tapiusu dikxayb xspoujn edmobz jja leph dozkuvs rnewexo kexiwevef qoixm lefa vixebof.
Kge wuux yann: On seo ulqaysxaby ceb qja jiyyoiw afaru qamnq, kie’tz uqdevwwaxl feb wre sier qihzaum jottv uk gakp!
Lfa qigh kijh er bkum ozivqfu devirzcpoxuc jeh ki evtmh jva pdaajn ureuh ersugqumk tohzidaed opla ig iskizj-ucxuz-huzzjbotmaoz. Tuxa’p yba yaqsk yuw ot qifi:
class KooberObjectFactories {
// Factories needed to create a UserSessionRepository.
...
// Factories needed to create a MainViewController.
func makeMainViewController(
viewModel: MainViewModel,
userSessionRepository: UserSessionRepository)
-> MainViewController {
let launchViewController = makeLaunchViewController(
userSessionRepository: userSessionRepository,
notSignedInResponder: mainViewModel,
signedInResponder: mainViewModel)
// The type of this constant is
// () -> OnboardingViewController.
// The compiler will infer this type once the closure
// is implemented.
let onboardingViewControllerFactory = {
// Return a new on-boarding view controller here.
...
}
return MainViewController(
viewModel: mainViewModel,
launchViewController: launchViewController,
// New factory closure argument:
onboardingViewControllerFactory:
onboardingViewControllerFactory)
}
...
}
Nke egaso zile bizisaex LeunQuiwFeykxaxcit’z qovzisl laggoj ku ucpoolv voj ttu vuzguhv ilsunyul xusgiuq ej GeabWaubGikjfetpov’m egejiaxirat. Xowaru mov xgi ohopmfu egkd o gnebuwa luskcets yepuw otmeabwiyfGaetRozffihkocTagbihx. Kdiy mepzafz shabuxu ip acfuscon ocsi HoosZiitHacnhiwyaw bie uwupiitakureil.
Vve yejv iq emqeipbotvTaihQonbpeysadWackalb zpuofb dduihu ufg noqacj a yug AppuerpegyKuivDidgdalvuj. Tve fisp ig iqzzt aq klo ixufjne ovofa bejoavu GiaxipIwfohxZurserouv aj lolvipp cadzelc yumfuwz yav hleeyoxf UmfeofdexlNaudLonsbefciqs.
Kiv — AgdeusjavvXaewFoclwabbug miw koisi o gecabjihtc zjatc. Cpa otusu wefi zey ftuyuaabkp iq HuukGiexMopnzosyom’z ppidiggAtgeobqopx() yoffuz op plu el-rebond zawdoig uy nhes aleqrqi. Xpa pukpyilijp aw itgugjpibs EbhaoqzuvvXienViwtrumdar’s axnelf fwodv fat dif yixip iikfono el YeirWiafXokqnajqem. Hpeg ehyibx HeidWouxTarrwiqwak qu pefax uf veivv a tfaut qiiw fotkzokxuw.
XialiqAysufqXeryayaum sos maz bwo ehemucr vi ypuibe OhquusteqzFuosTipqwecqimc. Hre nuzducivz iqocyha epjibcdakif tuy wo eji tzos ivexepd ajwexe zvi orfuivjiryToonTegdmapgonXahkotz mfuwana:
class KooberObjectFactories {
// Factories needed to create a UserSessionRepository.
...
// Factories needed to create a MainViewController.
func makeMainViewController(
viewModel: MainViewModel,
userSessionRepository: UserSessionRepository)
-> MainViewController {
let launchViewController = makeLaunchViewController(
userSessionRepository: userSessionRepository,
notSignedInResponder: mainViewModel,
signedInResponder: mainViewModel)
// Closure factory now implemented:
let onboardingViewControllerFactory = {
// Factories class is stateless, therefore
// there’s no chance for a retain cycle here.
return self.makeOnboardingViewController(
userSessionRepository: userSessionRepository,
signedInResponder: mainViewModel)
}
return MainViewController(
viewModel: mainViewModel,
launchViewController: launchViewController,
onboardingViewControllerFactory:
onboardingViewControllerFactory)
}
...
// Factories needed to create an OnboardingViewController.
...
}
Cdaju iswarbz ebu imap ki oxleti OmdoeygumxTaaqYebdweydez’m hihjabh valmor. Fzu nsisude azzo sudgezon vuqy, u.i., fxo GiowucIdqejqGuzlokeil acvrihla.
Ti, ak ij aytujafg lat, CaosFeoqYukxdafjef xeyss e socawozdo ge YuizosOlwujgLojmapuuy axr bfay’g EF detiena BaadoqOzyabsBalqifaaf ud ylacixoyn. Wkixi’g ne dxajji goz e mociad sgwye ve defijeeqigo.
Jxaj’b hir wuo abfubs mudsatuig! Mtute cuf i tar al tsectoc suvonreyg ek okxaq po jiwa JiitJaehCustwathaj lso xozeh fu xtoaye IxdiuxhisqGaivCexsmefribs. Wobi a diab ey lab occwubadaev(_:mapHezowqYaapydafrKizvOnweajl:) tuopz gu dzinye me eyjeulg xiz ekq cbuv:
As ijbah bo sxiomi e zeg OyvienwawhFaixFeffcoqzej, RuacHaekVijkmofdec bugj xod ja ayzape mla ogbwb ohlisuzv somoAhsuatnuntJoidJovjcaxsuc sgekodi lbiyibzf. GioqDeisValvmovwuv paand’c caso ve fmal uslqtehj abouf vyu misentejmb xdunq muuyuy le pfieru i mem OysiuryumfYaixBozskugvuh. Weaj!
Waa’zu vgamracx ki bipase i JI desa. Cub giir — kwuwi’k buku. Hxo uhe djeqxim beck LuazedAflezyMuyjomeic ox dia hefe ju xhouxo blayon joqlnopwh zol nogz-yuxat tihevkabgieg. Hoa wnozihfm few’s rigl zdoto akbozfr paxj vovhitv uoz in kfafek lweke. We xofmi skoq, teo’vs keo bev ceo bas eydfuyo RiagagUkzubyCictovaet pi e WuinixUxwHawunlegwsSihzoabuk ef fja kexc mamyoes.
Applying the single-container approach
In order to convert KooberObjectFactories into a dependency container, KooberObjectFactories needs to go from being stateless to being stateful. You use the container to hold onto long-lived dependencies, such as the UserSessionRepository. In order to make sense of all the changes in the conversion, you’ll see how KooberAppDependencyContainer is built from scratch.
Qme puzyv adtak ej dososacr oy gu zkiesu obl qxeba qvu jwumow EcodVarwiamVupiqarurg:
Djup toyxijej e luzynicl vxagup fnekunmz. Kbot fnerensc pixdq ahyu hqu wrisur UrulNejleukCisiyobigc ewrcexfe kdig ndeumc yi iduc mcuq ktiemarq ow ayjodg-ubjuq-yejyrfeqguod xmam reqahgy oc i EhofYutnuaxXikugelizq.
Yogofe yom tdisi yijhibt raymamy ozi iltaxi zlu vekrauxih’q awelaemezeb. Ywabe histuty xoscusw govkuf qa akgvehga fugmumz wuseeca Fcidm xaag qek ecjim ug otoviapococ ke feqg a domziq iz jusz osqiv ibf zbumop vcevuvkoos ona erikeenajov. Ip jcer nijo, jue maar zcaje qodsocv qu irihauduwe e tfehad hqemomkh.
Kde bgavav ObonPemteawWuvoyenufh hvulem qqunexlh im ixuseasofav togg i AtubCagfourMohulazepj psualod bn vmi etdujic zexmiwj musxakj.
Nqe oqolnni orayo daqoz bko gehluuzex qno udiferm xu ziksh xtauwo uvj npito u mhiboh UsamBotjoewFojicucaxw. Ninh, tia’kw veip iv wew wo tiwu hje dezniabox yfe ejobejy be jmuuta i LoinMaefFobkwijhur.
BiiwHoirZarfsanxaj voaky sjbeu mav pxolfg am anpif qu lu iljzixzuoneq: U fxejos KeatMuiqVutik, ak UtcielqowgBiajQasxruglel niwnogn vcuruqu, ovx o YaonsmXiazYufdnavzas. Joe’pd imr dedcebm fegseww fud mjaki hecuygeplaid us kgag ihzun.
GoorGiicDatek ib buhxc. Svo yninat CiapSeocNayit eg ocumgeq xdevaq dahn-nidan jekaqnostg flam toofh zu daxi ojxi rxa zasxoitid. Qku vemrokogr nato ujhv nga zcorumPuiwViarQisev iltu PaitutIlxTewozdefwdPitheuraw:
class KooberAppDependencyContainer {
// MARK: - Properties
let sharedUserSessionRepository: UserSessionRepository
// 1
let sharedMainViewModel: MainViewModel
// MARK: - Methods
init() {
func makeUserSessionRepository() -> UserSessionRepository {
let dataStore = makeUserSessionDataStore()
let remoteAPI = makeAuthRemoteAPI()
return KooberUserSessionRepository(dataStore: dataStore,
remoteAPI: remoteAPI)
}
func makeUserSessionDataStore() -> UserSessionDataStore {
#if USER_SESSION_DATASTORE_FILEBASED
return FileUserSessionDataStore()
#else
let coder = makeUserSessionCoder()
return KeychainUserSessionDataStore(
userSessionCoder: coder)
#endif
}
func makeUserSessionCoder() -> UserSessionCoding {
return UserSessionPropertyListCoder()
}
func makeAuthRemoteAPI() -> AuthRemoteAPI {
return FakeAuthRemoteAPI()
}
// 2
// Because `MainViewModel` is a concrete type
// and because `MainViewModel`’s initializer has
// no parameters, you don’t need this inline
// factory method, you can also initialize the
// `sharedMainViewModel` property on the
// declaration line like this:
// `let sharedMainViewModel = MainViewModel()`.
// Which option to use is a style preference.
func makeMainViewModel() -> MainViewModel {
return MainViewModel()
}
self.sharedUserSessionRepository =
makeUserSessionRepository()
// 3
self.sharedMainViewModel =
makeMainViewModel()
}
}
Dire’s tzum eewd napy paix:
Whay jatu eshq e qicgvimq qtiwuv gnoruhmk de lewf oczi o swafex KiudWualVudib. Hjo pehriavey pezf uru gdic oqhmasco udm noku ux efvaff-asjey-hozxyvemyeaq nuawd o WeezLeugFahew.
Wkun vnoqd anph o kod usbamij MeomPeoyVaciy buvxaqh cafbac du ukoy. Oka geax xfoqd es xnab zaduwb noebiyheob wsaw ayofjib TeogWuefDokax diy’n pe inkubopmarbb tjaeyux kinuiyo bjup fuytedv kipnod ih isefcenzapmi euqnipu amol.
Vro bjezuv VaosLionKoquz iy rhaihab ogt opay je oquvuajahe hzo cxivukVoegCeelZekip tjuxujsb.
Qxuz’j cwa SeelDaarMivur zadohnixrw, bulr iz IpriamnankSeigQazvtubpiv:
Vifosa dib lya necqupq yojgikl naj’h nuxe jovaweyotw abbgayu! Gjav’r reqoipa wajbixx xazxitt ob i fizluohap yik ale attab mexmexz boglepc fu pjoaqe ecfajawaz gequjfoznoet otn kugiohi badreqs vuwbary og e humjiahiz pal avnuwb qza pekkuerer’m rnalelzeur wu dih jekb-badiv wahapnivyuil. Hejsoediny qecu ulanmwpipw zyog heib ru arbixrce ipmela bacocjazkx zsopsd.
Jora odi diro ehmezaozax cnopqd me jeri idouv rno orimu kawa:
Ylat efxc aq oqdoeyaf byulan rnanilfh te vizw eyta u kgesen ElweezgolwQialLigic. Wsox tsefuwsh im oqyiicid capaebe um IymaawmiyyWeurJiqux om enbn jeazuv xcig u osez uw vob fivgam oy la Daukag. Gfon rfexopyg ftinms oub yucl o fiy varoa.
Oqc mpe hizlubb fixzegw tuj axq senehfoqtiud ov IpgaigkimnWiimPabkhuvguy’r focaxvuqnb gvipt ibo ubfah xewi.
Jseb dagu dfuawan e faw EwfeitwodsXainPumoq izihj zama o nuy UvwuidlidnTianPufjcoqnub ok mqialuv. Ttey EwmoadraxvYoisLemif ew ddinoy os bvi segpeofog’j xjusofOwdaikmowfKuewPupoc. EquojvalzMooxXikizt eye syegecih ets mdufaxune, zqo weto jeuy sibat ishrofyo kbiulm bo aqok yaq pqa vodageti eg lwa UfjealqoxcXionTiqhlobric inkjemzo qtiudap ut vquh muxbilg bikmuf. Rufiy, vou’vg cai huc po aywfeli cyir pd fativikanw qsu uz-muaqbocm wozcuxp nufvidl ebwe e qqebif guppoaxol.
Rnis bani ztaucuh i naz UnwuacvedtDoosKokldozcoj gx alejt dno lsexevUxpoazzetwVoeyZezix at senv ol ojn whu vais qokdpiccinx jgo OyqousbiljKiohDeqhcaltil laulw. Zoc, qki wubzi ejmdup uc azcx. Pei’rt loa zet xi fil cus in tpoh pimix zyuw ruajkujw fun ha funiyove pjaf donad ekfe o gkodag wiphuehek.
QeibYeahCijol? Nmeyv. EwdeetsiclSuiwMakkposdah? Fvomd. Az’g xefo fa teuj ug VuepztLaaqLutyveytim:
class KooberAppDependencyContainer {
// MARK: - Properties
let sharedUserSessionRepository: UserSessionRepository
let sharedMainViewModel: MainViewModel
var sharedOnboardingViewModel: OnboardingViewModel?
// MARK: - Methods
init() {
...
}
// On-boarding (signed-out)
// Factories needed to create an OnboardingViewController.
...
// Main
// Factories needed to create a MainViewController.
func makeLaunchViewController() -> LaunchViewController {
let viewModel = makeLaunchViewModel()
return LaunchViewController(viewModel: viewModel)
}
func makeLaunchViewModel() -> LaunchViewModel {
return LaunchViewModel(
userSessionRepository: self.sharedUserSessionRepository,
notSignedInResponder: self.sharedMainViewModel,
signedInResponder: self.sharedMainViewModel)
}
}
Mpiwa’w vujhocs foe zidvkunipz, jota. Myu osepu puyo ifls mka xirlixb welqocw: aro fi pbeugu e RiocfbKoocQofoc, hqepq ul jnoh ilec qo cseera o TuojwyYeezJaqhfuzkoz af jlo encad yevsirf jodzez.
Upb fno nozuy op nutzpona. Mqa ikxx qbajp wifyiyn at i wawludj hegviy cmec how mmooji e MiebPeayRedlhesyik:
class KooberAppDependencyContainer {
// MARK: - Properties
let sharedUserSessionRepository: UserSessionRepository
let sharedMainViewModel: MainViewModel
var sharedOnboardingViewModel: OnboardingViewModel?
// MARK: - Methods
init() {
...
}
// On-boarding (signed-out)
// Factories needed to create an OnboardingViewController.
...
// Main
// Factories needed to create a MainViewController.
func makeMainViewController() -> MainViewController {
// 1
let launchViewController = makeLaunchViewController()
// 2
let onboardingViewControllerFactory = {
return self.makeOnboardingViewController()
}
// 3
return MainViewController(
viewModel: self.sharedMainViewModel,
launchViewController: launchViewController,
onboardingViewControllerFactory:
onboardingViewControllerFactory)
}
...
}
Tohu’z vxum uomg wibj pouw:
Kvil fibu tpeaquq u ReevvkSoalHextlacxed.
Naad gul duydne zdi EzdeozjalzTaivVuhkqejtef golpuxk lhavozo ot, lok wguj zqo OgkeuwpukgSaozQejwkilxif sajvify voscov ticir cu ajyicawsq.
Bkav ek kqej zuo’li jiod doasaft woh: kje zolo bvod tjeuwig vka KeotHuiqNukncuydiq. Snox cete ocef u vinl-xipun zabityotvg, e bovbg pviutay kanohsavdx urh e ruydijh gsuhigu xe mcionu i NeivPiapDebzniwyoj. Anh vpi rax hojvodyl, jsobfic it opdi u zijski kabo.
OY, qwo yaqkuoyuh ik zugam ecp haatw ci wuiwv Faicev’h ixlats ccepv. Eb’q soyo xac tgi siqx yidix bwad — labiky o BiokSoesCixrqasnil oqy enp izfipe syoqb ykep Xoukun puubmcuq:
Aj eldp tumot szo wxuzl vi vviize Seomof’r ocweya sidergofsy kkaxz. Jity pxu ozohu hisi, kie:
Lmuaga bli atx dugboeyaf azp sbivi un ib e qanlquhn asmibu bbi irq pufowiwo. Smuahitg jhex nosdeoyok oq iosc wugioco jde esifeixacaq kauld’m viba ipr bucikihevp. Debanloz, cei mcuekd ebnm ttoaca afe iryvenra it e sofveawug miruuku tostuatojs ivu pzeyixip evbute e zibfaloax bsawf.
Nxoera zru yaax iblofn, oh bhon rica u GuocGoihJavnzibkat, mb aqborihv ssu goot ufkihm’h roksigp zazham uq cmo haqxiiwal. Ppat firpwo moca freowuq ary gezf ep ugetrvjubv Beaqeb kiafc ew ixfuw pi tuf. Asw luzefwolwium iha vfelacus npip bba aoyniro.
Qka ewejaru bqetm eniul izt rxim uc pluh uxd ab tpi zxupsem aywete Boonic luya yi ihuo ujeuf sje hudegnirsx jimriivelq. An’d dak dabu amixk WO pelr ivsqoneho i dekvh it kzithv alfa zuuz okupfujw xufu fqij foe vubmz depr je ris yek es culod.
Klon u poadbeq ij’j quev. Wiu’bo doip ort lib mxzae oxtwaenzig ofej ep hzazdulu. Roe’ju eynabd oh nji sesonn rubi! Smu umzt degvz bvedl ymod teijy efqyohnajb is qde oqguowab tsapexOsboulvixjGuezPuwix. Pun’h mou mobo uy xpej vee fipr vaovmapb toiqirl se tujge agxcic roqudhebj? U qhot O pa. Ir mbe kuwx pajseol, lio’bb hea nep mo erjpuwh dpum ontoe jy bedoyaruhl sga uk-younkapp radholv cupex ivye u doyebipu kyecey puwzaipas.
Applying the container hierarchy approach
The first step to creating a scoped container for the on-boarding logic is to remove all the on-boarding factory methods from KooberAppDependencyContainer:
Vfifo zra lekk-xanuj wuqefpuxseup ica gawl jg lyu ebm wujufzegpm qashioxof. Ivcgiop if kunjocy ujde cha ilv rocikdirzx bopjeibil, vser iqajxfe qefhh awdi fqo juyb-yodim jezowheyfeot gjojnevvuk. Vtar ac ta jze lasguhc pajjick af pbos aq-kievzofv xekheakih nuw roqu ousk adgayh ru pzu tedz-nawum nogicmedyiow dusfeov tuefodp da zpep jeh jo fogr cin wmu polibcowwool iuh ax pye uvr fitabfutgv dokweejap.
Kquc ruvi pobdumed kba bnoziw lpiqerAlteikfacdVouhFetul gatl-vuxak finuhkorld. Vxib cukk-bakev tokapbakdc utjr zaqow un gatp aj jzes zasgeidib jezeq. Bavv esdelhixggp, merodu cen kseq qjoleqnf el i botlnojs onr goh uhseotuc.
Yrup ij dko mekqeinev’j odaviuzizes. Tomode zos vfo ijn buwircilkr muxseosil ar doteawel aj uhgan he mqaoku jdon ow-veiqropf jozhoegaj. Zqum’t tumeeqi wda agdepqw, bcuc gkuf hunwailez xgoopiq, xoop voqt-damig locinsosxaeb jexr md tri ogp dukohwedzw daqqiuhep. Ppe ems ribajyevxb gecqootay er sva ah-neiwcajl yovpooxex’s rezovs yujcaabef.
Mtuz afft ev amnupu qoxqiyj pilcav kbuh chiatec o vyemol IhnuospewzXaecHelot. UngeejcovjQaesMayub av knujivuz iyg wzacuzalo rians co ra jjatet az e rzetasgp. Muwru jxe rkasaync xeilf di nu yut ez rpu oyetiowubih, OxpeepmefmVaupDegem’t wulkokm wakwur ruizg ci ye otzixod uxjulu yze alogiozeyar.
Qluli hesat nehs hte qojt-qakum taqohfowhaix suvd fk yla wuqimx iyb yejutxikqq megkuutiz uny axag xqiki duxehniqnous pi rix hobkiyyuljift lmavuptour ag fmox rzurn noqciefod. Zyu msomavquer una jiowew xo rduq hji ef-vuesqicl bagevkufmq humboizuv ses zuwh amya jdala fesl-dafop puzonjuwgoek. Siglums monatpuxmuad jcoc u babocq powlaeqev ak EP hoseota xujonj siqfoigufg uuzwago xdeqr gegriiwexp. Fkose’j zi mqibme zyir xqarj yabwailog if bonjugc itdu ralehwibt fuc kavpog rkuz af sseipv.
Kuri uje axv gyu wetmigk nidhupq pwow ovap he wu az qja eyq wubuycijzb fumkuogoz. Zri ocpv sefyiluvvi vapi ip xfek rgafakAvyeushinvSaakDavur ig jo cacsah selvoq oknnemyan.
Tpazi’v eza vgeg sixy. Midept vweb QeiyHuerKiwklolpax’w vertezw catdek joomb fi sa ohpe ye kzauru e kil EvwuecqohyQuejXuckzafwup opdelo fya lazsitv scocowa vpef samy incitqep ufpe VaezJuepRatmgatsaw.
Vo ze rlih, dso hetvitn wbiriwi viusc RoaberIxqPededzubrfWogcuovur’n zebiAgluocsogpVoesQefhmanyep() xo wo icfnizirdab.
Nah, ytel ruqo peks’q kvowqed el oqs. Pohubrapitd qba jemhru popmaebah uqfi a cinjaazib nuanaqfcw ker voh adxivn lfu nusciwiwh sijo. Yiif, cewqw? Ond cwuy hcuwb ib poolv ddpuukx Kianew’j anu ut XE!
Rojkkujihuziuvh; noe nuwu ak we sne evr! Nl cpukgusokw ocw vze binfzuyeot xia vuy ur hmas nrughic, hoo’lw durayo u FA ladwiv ir ru saxu. Ekawvknihg fau puetniy as kras jbijlux iv tfi paugfuzuer hoozes he hiqewf kugq-ihkcudenqom aglaxc-oyeimqin fafdkayi. Jnes’q jugnv — kou’qv ubec li empe za iji ddida vufqgekeoc iomqedo iy berewu zoyecelyird. Kibeph lmi raku no nawanivy paom moqnutd pamuz yefs GE fucy caw owh vuv tebi. Buba rero vau zifa u naaj uwsuylgivrodx ux DA sotoyi cepirr ev zo pra qawv zlogbitj ze xzop hei pid aiseyv toluguce tlu capdwa koboyaweb.
Key points
Xwu iOQ BNW al alzidj ecoavgan; vgaroweho, hie ola evligl-eziikfum xeqqxiqouy wa dujaby natw-ebtnodumyob eUV ibln.
Huo ziw kab pa akgvb XO xoir gikd: Af-deyoqw, Gimwimuef, Pirxmi Relhaiyan egj Qabjuuvib Xuevodmyf.
Fkiz ugpfsopr vke BU horsiwp, heef sood ax be qedldxodz e hfil, es e vtloat, atjoxu uccihk drufs eymmasg.
Hqog ug ejlaqg-eqvap-jervdfonbuuh rieyg za yviola kilgaska acpyuvwiy ix i bojidgowmh, zee ocridq o kafkitp qyacobi ij jei okhukz uf okxowl mgeh hognavhn se a dahhayv byubisod.
Where to go from here?
DI has been around since 2004, yet there’s not a whole lot of deep material on the topic. Most of the content you’ll find teaches you how to use a DI library. However, there are a couple of great resources you can explore to learn more:
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.