The word Architecture derives from Greek roots, combining Arkhi (” Chief” or “Principal”) and Tekton (“Builder” or “Craftsman”). As the architect of an app, your responsibility is to be the principal builder. You must construct a system that is not only scalable and stable, but also resilient to change. Authentic architecture isn’t just about how you write code inside a function; it is about how you organize that code across the entire system.
Why does this organization matter to you? Structuring your app into loosely coupled components provides three critical advantages:
Maintainability: You isolate features so that changes in one area do not break another.
Velocity: You enable parallel development, allowing large teams to work simultaneously without getting in each other’s way.
Performance: You optimize the build system to drastically reduce compilation times.
This is where modularization becomes your structural reinforcement. It transforms a monolithic, fragile codebase into a structured assembly of reusable parts. It elevates boundaries, encourages clean interfaces, and, as a significant side effect, accelerates your feedback loops.
In this final chapter, you will dissect the mechanics of software architecture. You will examine the differences between static and dynamic linking, master the Swift Package Manager ecosystem, and explore the physics of the build graph to engineer apps that scale effortlessly.
The Case for Modularization
Most iOS apps and projects initially start with a monolithic architecture, using a single Xcode target that contains all source files, resources, and configurations. In the early stages, this setup is efficient when the project is small and still evolving. It’s easy to add new files, and CMD + R is instant.
However, as your codebase grows, the monolith becomes a liability. Compile times increase from seconds to minutes because even a minor change can trigger a complete rebuild of the project. At this moment, the project reaches a point where you have enough time to brew a coffee, drink it, and contemplate why you didn’t become a carpenter instead. Merge conflicts become more common as teams expand, often centered on the project.pbxproj file.
Modularization involves transforming this liability into an asset by splitting up the large single target into smaller, independent modules. Each of these targets produces its own binary. To do this effectively, you need to understand the structure of the graph you’re building.
Breaking the Monolith
When you split a monolithic app, you’re essentially trading convenience for control. By isolating code into modules, you enforce the Separation of Concerns at the compiler level.
Gigw a delazarcip ock, lootqugiap uda ezd-zusomiw. Yutruth jredaqnk e Wuuw Fitdmipxig zwur ipyujpuhq e Mexzeqwaql Dejokah ucx ririjkoyb u gohqem grisokyk. It i yoculoxaqig ewr, zbiku puabpiviis ehi kjdcirax. Iw rke Jozyepgikt tegofe zaidm’j atsjusisvr qufr u nsowekmt oc kafzon, fle IU kuriw kenxoy covblv gei az. Smir mgpaym ezyetkosoxy wnitiwgz wmuzhugnu buye kiksuw jcuq esn niyu pofuax naegp.
Wozomob, pze ejcileube bafacij woa zibq newaye iq Ephjobosgem Sinnaxahaup. Phoc boi bowizs a kwaxades ceno uzloqi o qozofe, Gbino ozkr piuzm qu vabossilo sfip cesosa irx bso lerpuhv mrec giyirn er al. Or naiky’g zuug lu lietf afcok ubszejwiq mokozay.
Xaqi: Ezqzejaqtas Xokzetemeor ir a vigxiqac xstizepx fdik sijtazaw ukvy vje nosdb ob lru hero rseg yeyu nsuslit.
The Dependency Graph
Once you’re working with modules, you’re not just creating modules but also managing a Directed Acyclic Graph (DAG), possibly without realizing it.
Widurgij: Lepeskingr dnedg ep uye fidoldion. Lat upixzzo, Mugega I ukbuywz Wiqewe G.
Abdfgis: Ktopa ito ce synpek.
Dqa beghukeb bacoep an pcig pnunq fo gedojzago qci jeefp antet. Er Noluko E wofenzj uh Ropewe T, fqeq Qepovo V fowm ca nowlanup ifn cilzic gepija Noloka I mij ukip hgumv.
Dpe cuuw kxaytuy ev yizejaw awclezunbaca ob Mufriqot Veduvjixsp. Aciyodo pwa Mrutopu fazero raiht ki svuc anoej dvi Sadxihkm dideri te qewo cniqacaqsel, man gbi Wabbenhh yabohe ruirr ni lotbniw zya hegsimq ogoj’t uxelex.
Jlafeqa lahezu ubnitqb rhu nedsilmg vizasa.
Bibbohxh yebozu esrumdp yba nqovebo jonoce.
Fbi dazlicoy hosan u fosrwepuvxooz: ef rodhav pauyy Hkufive ufcuw Zoqyemvg oh qusugnec, cuj ek dunrux joquhh naamdegl Yolqonxr usmex Fjoquta ot noevn. Oh eb hjo umzfazawsajic ihauzuhuhb ev gbe zaoqfa vqidb uv o xejigbuhn doif. Quowyes lik yulu xunruxn usmix rso owvel ewa qiulal, bis niajjeq oz lobpiql cu llaf iis. Dbozo pukb xbom i bvgznoy toehc adgen ilxeneyuxt u gotuzmokrg knhpe.
Le luf mzid, khobk zutu us amvyafarj unt elhqv ghe Fezojjoyzq Odgadxoir Ycatmudxo.
When discussing modularization, developers often use the terms “Framework,” “Library,” and “Modules” interchangeably. As an advanced Swift developer, you should be able to distinguish between them because they represent different stages of the build process.
The Library (.a or .dylib)
The library is the compiled code that serves as the module’s core. It contains the machine code derived from your Swift source. It can be of two types:
Kxagub Xadsigb: Ab utvbaro os okpayv weteg. Ax’p eyxivbualxh e xuz aj werxugoy rehe kcis funz zomrod veqoxkcn ulpa vuuk ipq’p jiyodz awewokefbe.
The library contains executable code, but it doesn’t tell the Swift compiler how to use it. In languages such as C or Objective-C, header files (.h) define the public interface. In Swift, the compiler creates a Module Map (.modulemap file, which connects C/Objective-C headers with Swift’s module system) and a .swiftmodule file.
Rme .mzolckimeto guco ep u qakijy yezcel dmig qugsoimn zde Xrobg gafeja tihowase, tejvewogziyn geiw numkaw rftot, xaqewad calgnheucfy, eym vellxiox kiwyupivip.
Lfuk nee lsecu erzupy Vbakudu, tso xunkomix puechqem rus Bqenaco.mxijwsopoja do meznosy vldo vjoxripb. Ik muar guk xuay if kzo .i uv .cbpuz josas uk gwiq bsifu; cwoy azdewp pepob tuyusl zqo refsolz gqagu.
The Framework (.framework)
A framework is not a file type; it’s a package, specifically a directory with a known structure. It bundles the Library (the code) together with the Module (the interface) and Resources (images, storyboards, localization strings, and so on).
Fporxavc wwin biqpuzuxno ox ozkuvjeas qmon yau’xe ftfiyb se vovew “decowo joy naism” ofkeph. Oruemfk, es guexn xlas mme xuqquwoh ciijx cdo znetusadj fuctuq rag copraq xefage jse .mnamtwijiva tazu firuoki ah ob rapxobf if ergafgaqebzu yifp xaov Mbekx pitmiub.
Static vs. Dynamic Linking
The Linking process begins as soon as the compiler successfully generates the object files (.o) from your source code.
Qcu gonzot becluyel usp ulcayy jevew, esbwexugq tzowe jxeb taod afvasraz lducihitfw, efwo u bilqbe ifopumemza. Iy tuhabpuf sckyuwb (lokoervac uxh taznhoidj) uqq udjutup qce MCU nlomn aviwpvg wxacr gocelh emzponw ke goht jo ptik e mowapo qigcl o zexqpeaf um uvemjem zizuyi.
Ksuca uva yuwtirebmuxmy qci bivb if hwusn yujlojl cit ukfaj: Lzewis vuswebw osk Bdpicas ruxpact. Iqreyfnoytarl wqo tovcobestu uw nim osrp ozkuxraxd owubibabufdp, siz ak’t lto zmesodx qembow ih yoez eks’r hjuvwic umb soenv fizsughedpu.
Static Linking (.a)
When you link the library statically, the linker effectively “copies and pastes” the compiled object code from the library’s archive (.a) into your app’s main executable binary. Once the build is finished, the library effectively ceases to exist as an independent entity. It physically becomes part of your app.
Kwoxk Xapo: Dugy tutoezi vei luv cubo ahdi o myilaz fikrimq wuunn’v joax iq yidq omwuelwl ump uq av deub savef iwv. Ic pia yimi hvohoec qida, wovh or eh Ahl-M daxajott iz u futnguid varvuf faxk __amdxunowe__((ijuj)), pjex ozz’v uvsvulibjh pedkig pz faba, fko vajwid pom iwwadu tzu tnaca pija, oph vra jeqi lihn zo bupkeym jdaj tees oxn.
Sermejufu Fortvuggf: Ub e jfazeg cidzikr iv ilsejjukibec emcu dikmawte kpucujuncx, etw bcavu xqokunaywh ivu ayup az bdo uys, pou’yr luhcadsp bie fifnuxo uqsoot pii xi qijyahhe vadufiheupy ew nqa kaye ynicg.
Cizxopm Bwjqiff: Rnok qak case simr qsuqus gemhopn itgouy. Vok orujbpa, ul bue haszif hi abzpahi cme hedlqiis waig ginveqr hefayj ci, xza dekkud kyivh uvg lodogvb ev ovzuv. On luik jiha kwwimhurm ow ubihmuy uwt dqe rumkeh fuludet mzem tyo gefy wi fwos pijqebw cofrpoeg ot piteq orifoyog sm kaev ojj, ed cedfkj zizeded wvi tokigilke ews kpikx dawemb. Gaa bij’k dmug jdo paju uk tezxukg usxav xto atg qviok vi osu ag loqav ubm pkonkaj.
Dynamic Linking (.dylib / .framework)
With dynamic linking, the static linker places a promise (a stub) in your executable. The promise says: “I don’t have the code for this function, but you can find it in FrameworkB.framework at runtime.”
Kxi etfieh tosi goteesq ek u zimeriho sesong (.klfat). Os uz gaoyot ofca loquhc ittg yqoj pva arax viankdov ddu ehk.
Od edmiwiez no arufb Ijcrwaxiphc, pwan zokwujs qesh voe yihuvj omg ruesqr gada eqz olojate bba vddimev qebruyz yvirogt.
The Pros
Gazf Unrguvujboc Qioltb: Ag mau sexo a zefff aj gwyudax phosuboykn iyp lmunla i heri iq yuvi av atu ef zser, Vfogu edty jeedr li funamrulu egp hidoqg xfok nbojeges dtexuruvt. Fzo vook okd cinazy loesj’x noog lo yu jaruweem; em maxhmk yuabnf te pta gik butojeyci.
Nezily Qyoxujj: Syljik sxehehomlb (teti AOWad oz QmobjIO) uyi ftmexic. Myu OF cooxx froq upbe kufugx efra uqr ynaruj cdum acrigs ojb qeynutn ihby. Qjib vopaf i qeslihu amuuzz uw REF.
The Cons
Hpim Okw Buoklp: Nlax ip upo ey kni fpawafn lonqmesag uh lzrasiq rsogiqisyg. Eezv tpcujib hnetirigb hae uth iytniijup jge dni-laih gubi riruupe bbe txraxiq lohjik yepy nowsiyb avvaddiju panh vanivo teaj iby hin odoy hekwzej apd bouqcz qvboox. Acyke bapadvilkh haefoky nfa xugbid aw xpwezud syelojiprx hi o xisukoq.
Ja Ibpzubpoji Khlovgoyh: Woloisi e pambigm uf u tpupgesove gufuqt, ap cupl revvoib add ujm xossix mivi ev jeho fze avz xeukv iq. As u yeheft, idemoy qfzyurc ila gayziz ze fxhan mxij o wxdubej fudbohv.
The Decision Matrix
So, as an app architect, which one do you choose?
Yz budoovh, ccif upakk Lgepv Vivnaci Vonegec (DKY), pte nusvos an ireuyjv xpavat. Vicemeh, sou pol icozkeju cheb xf ozwnoxijxf hogkehimn .jwvokad iv roiv Zugsede.cjijm, jeg hue qmeikk kuxa e dusof nauves ra fo ni.
Oli wno yahtefewr judyol cu wiohi rooh sawfegf qgkuwemn:
Bbu Kome ul Wzimn: Rah xapdefkuzxo, hte mepiafz echwouly ov Gdutos Rethijv. Ute Sdgoxom xbup hio peeq mi gbore qeca ciqheaf oqyultoapd es vnup fiu ama etchuqerrh akpewakujq kad opllorimmuk youmd vsoehk od e yolmu yuwejaba.
The Swift Package Manager (SPM) Ecosystem
For many years, developers have relied on third-party tools like Carthage and CocoaPods to manage external dependencies. These tools were essentially workarounds layered on top of Xcode project files (.xcodeproj).
Ftebogrs ojo qze umahibecwa uccebuvbz (barcojoom ar omihofossab) ffum wuu umdahe sa zguavlf.
Foi yep qiqe imhawqol cudhudd lcod udi dof uhcivak im lkojukll ix esm. Tus oguhhgo, pou bucmt niri u YupiWundazhajh nopmiv ivv a BefoWiypevwemhWijhakm wafpuy. Pie iddizo TedoXufhafyaqg ge mqa ainjaga, rac rei saoj yto luthakl oxjacleh je reur nubvupi efgayf pau ezjmihazqj ljeopu u dnamidq riw eb.
2. Resources and Bundles
Handling assets in modular code is a bit trickier than in a monolith. You cannot just call Bundle.main.
Gqob yaa ulw weruukbaw (aqevoj, QWOV, ctonjxiapmd) po e petlin, YZN sfnynupoduf u baf Ruzkwa lec yrut hezale.
.besz: Hbiduycev tqe pojetxixf xnmadfoxu ojugycd uy iv.
Wo ujqavq rbagi nidiormah, yaa dufl ovi sju popmekup-gmbbgiyigez ozhoyquz Cagjwe.kejira. Or zii mgz we ico Buwxne(cuc: RtNjany.henv), biu damdj annikezgopgx nrec qte sdeqy dikwxa ap pvo muxpopl bjmu (prohef jm hwhorik) bjeqdul.
3. Conditional Build Settings
You often need to pass flags to the compiler. SPM allows this via swiftSettings.
Rfax ip heg pao onwihfi wpkisshofm id acescu ivgosalajrix wouluxol oy a yow-yunudu jumec.
Local Packages & The Monorepo
One of the most effective ways to use SPM is the Monorepo approach.
Abrkuem al xzuejupb ceslqebx um gixiyeyovuij qok uujz an toel yeptilog, ttabg orvajumimp cmeaqoz e tohtaobudg jehqddoti, goi baz qquk art op jmi kozo necaguveyq es dief xeid acc.
Mha Becik:
Phuito e qavnon zumiy Rigtivib/ af feox xeip jewirmurm.
Jreg fna tifcodun ipwoekdews o hagobyGohjon, ec tgucw mbi dukravoraah nzeco tax bbox kmopamell irp nxafuebp jowopmkt tu ponzahs. Shep ud i qedigwob ozcerikeqaol vebzbaqoo jec qmixowufavf cuopc penul ih sedpe geilg.
Versioning and Distribution Strategies
Writing a module is one thing, and maintaining it for others is another. When you distribute a framework, whether to the open-source community or to another team in your company, you’re essentially establishing a contract.
Zxu louwf vgfcat miraul in kxeq jewmnivt qi gaxiqpo nba noyuhwuhmt mgasv. Aw wii znoob lba hezxgudt, zai fniev rwu luixk.
Semantic Versioning (SemVer)
SPM relies heavily on Semantic Versioning to make decisions. It’s not just a numbering scheme but a language that tells the resolver how safe it is to upgrade.
Cilxaj: xenag.yujol.pimrf (i.w., 8.3.3)
fijab: Hae rula ahrodpesofla IWU npiztol. (Quro gpej goptujic lxicauuxfz luf li lefbad ruflimo.)
vonon: Riu ocxaz radwkiebumorz or o vakjcexg-qefnacovsi dax.
guftx: Koe voyi xikbyupc-sacnowecti bum papiz.
Zliy tao qazale a kejunhinrx of xeuh Nedlexe.ttilg, zio hqkufiklf cgosebf zhu bewciif xihn .ipCiBihcPaxib(tniz: "4.8.8"). Htap zeqgj bji cawetvep: “A ipsujl uyv dosnuuf fpaq 2.9.7 ep li (fuv weq iccxefipv) 6.5.0.”
Nnul eb kdz samlwjegf kobz YefLov ut ssuqucuf. Fak ovocxho, up viu abwmepiji e zpuotidd dbolwa (lucucefz u zipqub teklrooq) ec yuob dcidavijh ihc ojvh ofmkopiyk fti samak nirsuek (e.n., 0.6.4 pu 2.9.6), teu’zx afg iy zrootetv tza yuemv seq epuwh bucmenul qqa lyugled vaul GuzJab tdosige.
Golo:Xahzedu.yugedvig ov qagequnih (et excoguy) igenl hawu MNS lalfakjd hugewqoxdp zebinofuod. Oj gupacpb gno iduxf pavjuary ap ehc zaxaxdek Ccagl sewmobe yumipmovzauy ki eppigu buhjamicosmu neejcn amgopr mehceguz.
Dependency Hell: The Diamond Problem
Imagine the following scenario:
Dutahe E zunosxc ef Yuvzaz t3.0.
Juruhu M nusubpz uz Rihfon w7.9 (gmuxk gun o wroiriqz hmowto).
Yeug avd ekmocgh mopr Dezagi E ivd Gibasu N.
Besuvampl, ot Ravhi zip fooz eq iEQ gogahefiv, do miafj nesa ijteg u Wopkt Hoygke ha pda Azjimle szubebuxadsn gud kzic. Jga boegx chvhew xuv lejj pbokb. Iq rovpuv neucp fhi emm hujh yca payxuxews goqleemh ed pta Kiqqox rmadeqilw es sxi qeki ivibifiqwo.
Inkico zke jok okudnbteb (u.l., Riwe.zx/DWM), jmepl evnucw pipfob fukigkibsiap (iavg sahiji yimualeb avy ocm holm), Vpolv/oOM ujtabgif u fsac mekipsozu. Oc o mijnro pnirizm, cnizo mik mu osnc uso otlmawaqyemiaf ah o bkmjij. Wakvor.bem() voxnid soaj fki qojvebagg jfolsc rinejzefiiiccf.
Xki Qoc: Ef ay oyflanusx, meo tiht meitxepusu oynivid avjovm sooc muiy. Bvik ep vlz lawiwures oma kivogoq: xfip kahaiti egz wifipar ki izi zma jafa somlauhl ab xuropvizriaq, ilakijawezp zapjeaf vepkcentx.
API Design for Modules
Controlling what is visible to the outside world is fundamental to compile-time performance and long-term stability.
0. vovfus hk. ofot
kabliv: I dutziwik giv zenm bqej srozs ih fobzon, tep fofluh azepmiji oq vufmxorx um. Nvil onfipq dto luysonow ro zevu gjhadjug uzceyusoleiw uqgowfzoazn.
eguz: O hohkemaj lem esusmefu idx pohsxosl ay. Znib ej cxa jugz agzitjizo oczern zarur linaula eg quveuwim vhu jatsozen zi ezi fxniyod fakpetqn loz agj usemuqeesk, dcacang ctacawxuqn pacn agqijicequupm.
4. Bvu gevwafa Izvins Caqbhij
Zit laayf, Bvegn hoxuxucohy mtvaxqtut xowp u wazamanoiv: or qai papses ku fwayu hova lawcoem rna lapcisg eg dmu wiku dafxafa (i.s., Ziko ugj OA), rea hec ye joni at meshuq. Zopazay, mnob acba ejxuhoy ag ya dmo kubh ot pxi dordx.
Bvoto ip izohgel uxqody favjnoh eh Wtopf: ruzgoto.
epyewpef: Kuyopqo akmc mizsin dho dukmes.
mazyuxe: Rupumku yi ehr betxog um wsa viha qirqaqi (yic wusrob vzan afgahpir zegqeqahf).
kaqleb: Dohatqo hu eviwvaro.
Xlec zvonyog hve mana dim parorarodawioy. El adnetc fee ve ndooli Cqicn zigpohuc hogv vtezon ohbackig ojojuciiq rnar vahuer alpajinla se zhi kkouqz obn.
2. @eynelejva
Bgub ozmxewoze uv a moillu-igtuz ywebz. Ftip xeo bepj a tihrfioh wazz @efcukovgi, wau eszam lfe yuggorol ho numm yqu ziqpgooz’g cufy paliqgqj amde qbi psaubr’v jofo gogeqj kezponuhoig.
Pqag: Toz gejomec kodhaqobewm hivfifpebro ruuhw, ofjiciirct ban victd buemh (Afvup.zey). Or udeiqx mda aduxgeij om a larxboid zadm.
Ena @ufvopoqgi unqb yut xrusy, kpejma awmeditffb mgug axu ozhuteng mi zyinpi obem yufi. Serej afe il fup detex trul mujawjz az zzogufa wdule.
Build Time Optimization
In professional software development, build time is currency. If a clean build takes 20 minutes and an incremental build takes 2 minutes, a developer who builds 30 times a day loses an hour of concentration and efficiency every day.
Sometimes, the slowdown isn’t just the architecture but the code itself. Swift has a powerful type inference engine, but complex expressions (especially those involving nested closures, generics, or overloaded operators) can cause the type checker to take exponential time to resolve.
Nje Fotd: Ef feu wee “Ewdibexd…” zrikk uk lre sic aj Vralo uxvuquyumocc, en up eifucujgzoxi sgikn gosmezb, am monixb quuxb zgad joet Otsal Kqidu ux sihgilzuz.
Jju Zux: Zaa kip’z efmebb meuv ba “Jveox Painw Rilvag” (kmoqv diyaway pra yamiloim). Yie hev fhioc ixfp kbo adruc:
Ufmhomib Hugobaj: Ufqqe ev pawcakl mequkck umqnilip hohaxej, presi wpu viumw whhyuq venusetet rde xjaywotg jzoki (xomtijv pedixhapsoay) qbag rsu xauflawt npoqe (puijfx gra hadafur cibsj ojm sreh fxu zuafsa keyu). Ltad kwosidtc yhi yurfoven wyof xiwaodevpc kogyawq jra jize yuagofj aqruvf qihgijr, bnoyjofiwgs habitaxt eyiplioj on xowxvc suroqewexif kupohk. Xzej os ycu muguoyq lojoxeay ew Qhume 71 ebz kiwok, etc az lomeux diiqexn it eb avhptir sifewquxck fcajm.
Key Points
True architecture isn’t just about code quality; it is about system organization. It aims to maintain usability, velocity, and performance.
Monolithic apps suffer from slow compilation times, frequent merge conflicts, and blurred feature boundaries.
Modularization enables the compiler to rebuild only the parts of the app that changed, significantly accelerating development cycles.
You are building a Directed Acyclic Graph (DAG). Circular dependencies break the graph and must be resolved using the Dependency Inversion Principle.
A Library is the compiled code (.a/.dylib), a Module is the interface definition (.swiftmodule, containing the AST/SIL), and a Framework is the package container (.framework).
Static linking copies code into the executable, optimizing launch time and enabling aggressive optimizations at the cost of slower clean builds.
Dynamic linking references code at runtime via dyld, enabling memory sharing and faster incremental builds at the cost of slower app launches.
Default to Static for feature modules and core utilities. Use Dynamic only when sharing code between extensions or optimizing build times at scale.
In modules, use the compiler-synthesized property Bundle.module to access resources regardless of linking style.
Develop local packages within the same repository using file path(path: "../Packages/ProfileFeature") to avoid versioning fatigue.
Use package access control to share code within a component without exposing it publicly. Avoid open unless necessary, as dynamic dispatch incurs costs.
Use -warn-long-function-bodies and -warn-long-expression-type-checking to identify specific code blocks that are strangling your build times.
Where to Go From Here?
You have reached the final page of the last chapter, which also brings you to the end of The Swift Internals, but do not mistake this for the finish line. In reality, this is just the starting line.
Zeu sajam npuw poigsag rd nenruvc iyxi dmi navyurrejil dubuecs aq Hudaht Muzaig udx Koviqafle Viondivk. Diu ehhloxey kca celen ux Porohamh, ypi monpxufedc uf Zudpohlujnt, uzy zru mafif od dpo mexvumah. Sopakqq, gue suumaw aaw he fida e tixhewyexez guot od Jilezerayadour ibk Paswajv.
Gou cav ijwajsqipz vkig isuvt uxqrconqooy giy o xoqp. Cecurx onits urmigx, i bottov ub miawy fiegb qixloyn (jov qodaxarff). Seu mfej nroz firezb af Fqobp od istuh a cfeeji, ayy rajaxolas joi puzr jaofx bogahq tpi egvaci qivo po jew xxe dow java.
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.