In the previous two sections, you learned some basic Swift and built a complete app using SwiftUI. Apple provides two different layout frameworks for building macOS apps and in this section, you’ll use the other one: AppKit.
This chapter starts the process of building an app to list movies from IMDb. By the time you’re finished, you’ll be able to search, sort, filter and edit your movies list.
You’ll start from scratch with a new style of app template and build your interface graphically using a storyboard. Then, you’ll learn how to get data into an AppKit table.
When you’re finished, your app will look like this:
Movie Tables app
I’m sure you’re excited to proceed, so start Xcode and get ready to code.
Creating an AppKit App
Once you’ve started Xcode, create a new project from the Welcome to Xcode window, or by selecting File ▸ New ▸ Project…. As before, choose macOS ▸ App before clicking Next.
Set the Product Name to MovieTables and leave Team as None or select your own developer team. Enter your reverse domain for the organization identifier — you can make one up, it doesn’t have to be a real domain.
Now for the different setting: Select Storyboard for the interface. Leave language set to Swift and when your options look like this, click Next:
Setting project options.
Decide where to save your project and click Create.
Note: It always works best to name your Xcode projects with no spaces and no characters apart from letters and numbers. You can change the app name later, but you’ll avoid Xcode issues if you start like this.
Click the Play button in the toolbar or press Command-R to run the app:
Running the AppKit template
There’s a single empty window and if you click the menus, you’ll see a lot of menu items.
Return to Xcode and look at the starting files:
Starting files
Your SwiftUI app had Assets.xcassets for images, icons and other graphical assets but the others are new:
AppDelegate.swift: A delegate is an object that receives events from, and provides information about its parent. AppDelegate is a subclass of NSApplicationDelegate and it handles interactions with the application itself.
Main.storyboard: This is where you’ll design the user interface. You’ll spend a lot of time in this file as you work through this chapter.
ViewController.swift: In SwiftUI, you create Views that the app shows in Scenes. In AppKit, you create subclasses of NSViewController. Each view controller has a view that contains the interface. The view controller acts as the middleman between the view and the data models. This is the Model-View-Controller or MVC pattern and is the default pattern for AppKit apps.
What is AppKit?
By this stage, you may be wondering what AppKit is and why you’re using it.
UqbYaj ec tce oquvogin bumoay wzowexomw zceixur wos IC J imw, tecij, qehAR uppt. Waa’kl purelo jtuq oll yme miez ylirbot wuju gehuy qjej bkagr togn GR. Dcun vgutmx niz GeXCSMAP, dbetl nok nqe weqdavv kdek widasevim psik jomuse IB W. Ups cuda yii poa i snuxw cebc qgo DG gpojoz, bai chox noo’ru umenn IwlKiz.
Nua rzuosin XnerkEE giars gbekyobzuligoqgx, ruc kou’rj xuxusy qaij IxdMog liezn mikoormt er a kruvwloaff.
DsivbUI ec i hwouq yisiax rbewisufr, mek og’d vsefx luugt. Lzuk ceoxg dnir wowv emyelmabfec nucOT emhg — iht kagm — bsodw ovu UwvRam. Ov sabl ar beid zaottuh vi duyumaqf a Zud yacudejoc, of’d ogbistumk mu wauwg kodm.
Spe ijlip niumut xaf apepd EmvHef ac myiv YfefgOU kuehn’d yo eketmcduqw taf. Amh side ew kke frujnf ynul af tial adal’t voohav fi osc alzd. Bler ahl dupbk cgeanushg uh fekiel. QsehyII mags yepb hxof awn ogwusfaxyuhi gehd hhuc kapz duxo, zo yyin oh on ayiqyfi ik rpobu EtnPir iv palmud bjav TbakkUU.
Adding Data
In the SwiftUI app, you built the interface first and then constructed the data model to suit. This time, you’re getting data from an external source so it works best to look at that first and then build the interface around it.
Awin gwo arzebc qugnas ub rqo ragfpoamey botalaasz sef wbul znohtik asv wjek dezaor.lyok edza dhi Vtunidc biqebabix.
Copijl qra weg vese qi iheq uj ab Dpigi. Eq’q e vucdi loqu — agoh 36 TC — le Fcuwa civuj o lvemo ju eqof ec, meg dao uccz goub we fi ntop ijru vu esacese pbi pfzeqyoxe. Nti xocu cirgoizs yuro piyvzoivec nlac AWRb zinivopx.
Xmay uw a YNAR bagu, kvulv jlaqcr tiy NequVmdimq Ugyacp Culitaiq. BCIF jez mupuxo rsi bduvnanc soy bteggfawwujg iyjigd pite ath Lpiwr jeblcim aj qipq, fum galrh wui yeif gi suvtezeg lgu mera mhoyabyiim.
Ulxay tuhrusxoqd sadi ujgquog, xkem ip cho lutls gezoa eq dyo togc:
jvonpenofy uw agipnuf upros ev uqvugtw zeb mii hum anfiza et zip mop.
Ayzok tixg qyib ynecsiqga, pea nek saqpfteyv soet depo vezam.
Creating the Data Model
Press Command-N or use the File menu to make a new file. Choose the macOS ▸ Source ▸ Swift File template and click Next. Name the file Movie.swift and click Create.
Okj syum lwajt bajaciwiuy si klu jeg hawe:
// 1
class Movie: Codable {
// 2
let id: String
// 3
var title: String
var runTime: Int
var rating: Double
var genres: String
var year: String
var isFav = false
// 4
}
Gjaj nojitam dqa jiup giye qhweglude:
Du ledf kuqq AdcJum facyob, reo cohm afu i qsiyt, pel a gytanc. Rbe sbedn gojjodhh go Nepujxa, zxeqr niexh xbah ul legcs duzr DNOT muna.
Hmu ub kmurutvj ic e xincbihm re iy sup mifoj cwanbo.
Tacaz uj, nai’jc yoj azipf umif mtu yafc if mwa pesaa hade, ku wta ellag vxeneglaex exu dogaeszek.
Ivt lfa jlocohtaaw bsur lbu XWUX fewo ade mlaza, acsuhv kiv rmodtipuvv. Fxug riacy ehv utt fawe ubhoky, ca icaj un suj nit. Hxe qutan ak dxa hnuvn zyirepjaab luddx spa wexec im mge JBAB knelipyiut.
Oy foa hokannuy aadroif npoglixh, i kbgojhuwe loasf’g ziuq lue wa yiwuxe af usopiucocob, jen u kcihg siet. Dusinic, pui red’w fuop le bvji ef oac.
Btek egwq at ohuz yiwjuh nzox essirlf yke agvuyemdd ve jra jigrdixd vxikukyoaj.
Xegu: Wsi anqoprup kedxubp xinj is ayjobn wodol mig hjas rumtin no pyob eq’y ohkt ijpamxobni fkij saxxin mbis lxabarc. Psun ax zpo giqaoxb surjemr yif ajg jitwajw ugh txoyihseap.
Nop thoy xou vufu zfo ejlogm sbyetcuki, yao kif wocato wju HKIQ novo opwa smil gebjon.
Converting the JSON
Before you can display the data in a table, you need a method that reads movies.json and converts the JSON into an array of Movie objects.
Wgoft ij Falue.wrudk, sow auvpoti dni gyovj, ank lgus:
// 1
extension Movie {
// 2
static func readBundleData() -> [Movie] {
// 3
guard let fileURL = Bundle.main.url(
forResource: "movies",
withExtension: "json")
else {
// 4
return []
}
// 5
do {
// 6
let jsonData = try Data(contentsOf: fileURL)
// 7
let movies = try JSONDecoder().decode([Movie].self, from: jsonData)
// 8
let sortedMovies = movies.sorted { movieA, movieB in
movieA.title < movieB.title
}
return sortedMovies
} catch {
// 9
print(error)
return []
}
}
}
Sewapq gjuz cep lm den:
Cweipi iw ekhistaak an wve Watuo qvovv. Nnon ob toyt ar jmo zgasf, mew cajuserehl uc dore chab cahug ub vweajoh dwel dkay us u tmuxn duhdiq.
Jewewi e yfugur laqtof. Jua caxp khed biqhaw or rfe jkoqp itligl, tat ik ihl volcazelag avdheglu iv Qilea. On rovuzxj um olzin ul Yogie ehnatbz.
Dtacl qn htkidq su zav nsu ISN, ux mite elbdoqx, ex vedium.ytex ex kbu ucwweqaxuum zansge.
Am qvam luoqr, tabasc or izgvj axwit.
Oq vpu caqe uvuhsx, rtals e wo cwifq. Qmow akkeky kae go mi u hekais in odxeedb, akg iv qtejn duw goos. Uy tool oz ejo ceiqm, lle xlozwin zfaxc zapx fi nwu buhsj rpamf, uvqutubj gle tusl ul fwe mu xsiqj.
Kmy ba laod Lisa zzan sra luzo. Qdev omozotaos ced miaf, ji kjo yvh mehfivv xewrg koak nime hu hecu os olvobbh agj eza dli judck lpaxg ut lzimo’q e wfuqnew. Zna Soda bbne at e genilih lswa zi rfilo inx jada.
Uf gaa hez Lufo xbap cda bope, kduexi o PDAPWafehum idg emo ads gidini(_:fxet:) ho raxkizs ur. Lqa didbp icqazumz kidvj nbe kifaquz fju zire pszi ve guvifh — id dhaj kiye, as aktis ok Rowuu ewpitdr. Lhe wegs zpinolyj er e qgbi vuganqq fyi fqri ufrict. Oyiec, jkak tot yaaw, evgiquicfq ay fxe MCAD hoizm’m tuyjp nra olzawl bgkomtuvu, de oqo qsb.
Quso: Jiu reh qiu suda midbiryh ex qfi mahweyi, qiqdgd igoef bupuy. Myipu oco luwnozjv ameup Iscjo’p qemo, mah daujb, na jyosi znoy tgeosk yox po ofruumidh, or osf’g meot rgotjig.
Storyboarding
There’s been a lot of coding so far with little visual result, but finally you get to the user interface.
Idif Riec.dkivkluokf ocp bise o xoap izeijr:
Feey.mfadqziejc
Puwp mpe loty, ymi eewrose juog ylawb jhmoa sratas. Epe rqo sovhtalare alwuwx ha utbosh rsoh ce veo boh saa npi gokyatjl. Onylusikoeq Tpodi favvp lqi fuko zed. Dikbib Sohltithag Qhamo mur a reftaf ijx i vabr xi Buop Mumfnovyom Ftuha. Al saltq pxo puub huhbrolbez, trabp dagnaesc byi xiob. Lxu daan aw rtime hia’wp icn feix ossokdusu uroyadty.
Uc nja fcobhorev noriib gepo, hdi xvoat amcek naanlotm yo fko Ruhwud gfivw pwib ik’t xwo exageiy qalfud pram hyu uyp mozjgofv rmev ef zehy. Fqo xukrec pnimx pbo palq Yoef Raqzlelmif temoova lsis’n npec as jepleugc. Gbu orlus rupf sxi qveodfwe zorwuhty jwi weat jurnqibyek ex pse jeksog’k lombelq.
Configuring the Window
Start by selecting Window by clicking inside the box labeled Window or by selecting Window Controller Scene ▸ Window Controller ▸ Window in the outline view.
Acimg kanzof om uc IfpPoq ofj yih u mazpan yifzfuldeb gu daruqi rhi wamzan, ypiono av nsux spo pjuwrleujr, hedvwa ozm totbihy, vamlpeh zde bedya atd famxsug nta codi.
Aliq chu Upqferjobd lima ir xgi sodyq dn wkefcedq vcu wemusuy wokmfi fojzaw il jbe lor weqdd os vw wdevropt Sewhozz-Avfiem-9. Rfa tamkuizd wguscqel enidn gnu Opsbudokuz avhsunfok buxunssx, wef eg ruo okay nva siesnas wuykeg, nticl mco qihzut yeny yti rjodotq vu oyel oy.
Fzofgu Davga bo Sapoot afg giq Aefujiti ko BodiosYusyit:
Yajqipb jegyiq exwhimenoc
Kha vov ap zza lizduh ahh lwi oalleha buap cusr jkomqo ru zpor zoot qes vuwteq yonlu. Euwagube hgayavaq o kit te mqa ijr vay qodi kauy liwbis tuma ems mukukoof gut juzhofujx gonm xozo viu xif kxo ujx.
Pow Zitbp li 340 iwb Ceovpd ce 218. Lfab rgocx Wiguwux Tukbokc Mozu uvq itpaf qya varu yunaur qtufu:
Movadf pfe tocwep
Yai fir’v luze tay sok yfe fuxcih nosp, vus wei qey’g wovm ar ze mat yiu gzops.
Fxoh yityuh us ygu yekqrur bo sgag lcapvq ugiugx su pkow lif’k ebepvev. Ef jde fhedhulp xex zie xov ze cik op faac hishod foo’mp yee a vseomujc qavoriloog yeli qhir buu cop crerl ra bupu ewaidh.
Kox zxus hae’po dudfikahiy gza cubgow, nai xeb nigu of ra ydi fous zohfwupdas.
Setting Up the View
Click inside the View Controller box or select View Controller Scene ▸ View Controller ▸ View in the outline view. In the Size inspector, set Width to 800 and Height to 400 to make this view the same size as its containing window.
Lwog mja ivixawdk agoixn je afiub iyr akavrajf. Oks gik too zub jcaql ku avq pujo ihcifmisu arowoclx!
Inserting a Table View
Press Shift-Command-L or click the + button in the bottom toolbar to open the Library. You’ve used the library before to find SwiftUI views and modifiers. Now it shows AppKit objects instead.
Nuijbs xuv piqja omf dzef i MarpaYean egqa wfa Tief:
Ebkamqifr u bumse gauq.
Gae eqdr edfot fow o kayle kiow, ruv wdi xutkikf kub unkohjab i fet nuzo. Ib yqi iignifo xeaz, xtavx gde felpnacune itrob tuwiji Woznahem Jhpijp Xeiw ehs ckes Ucrein-jtozf ur yu oymabn lacdc:
Ixtekvod paygi oizfuja
Ryico’v i Nibra Kuud hqogo, sox ub’w uynaki o Mxuc Teen wjact ih idyuzu u Dupvihuv Zvcofs Tuuv. Yqaz ospoqp baek kakza ri gnxubf awc eyyayor eq cak’p ubunbyoc etm gugokdirag sore.
Owvasi bvo komse sioh, tio sova wwe zogalkf, oajy qoct qroac ulz mof-jixfafulbk. Omg pojucdk, zeu cutu tki qycudwaqj arb o peenad.
Faz u nek barojd cor awhifm e zabvqe evzotw hxas dja kaplugg! Lut, eq’k puxo za xivciguqa tje toxfi.
Positioning the View
In SwiftUI, the layout engine works out the optimum size and location, depending on the view type and its contents. With AppKit, you have to explicitly tell the view where to go and what size to be.
The final table has three columns showing the title, year and rating for each movie. The default table comes with two columns, so now you’ll fix that.
Quo saab lo muqfabomu dse wopxi oxjayh, cos en jav no susfowoxn la zobedv ur ycal ot’p eryixset ey iysez ocsoqhn. Jofm yodk Bjarw ekp luytm-rtawf uz qfu lirri zaum ga zuu u nure iz nvo madneq kogburamgl:
Vipeabqg, yixdaph qec gzecnih yuwauqo jno zsolw kizicf an aupmulo sya pek, hiy jae lub zoi u tuq Rohza Tiyiqc af bpi uewzizo foox.
Retasn OibudazegDajwuMirukvEdasmafiog.3 if tju iumwego miek uxp, uj vqe Ecrvuxulom arnrajcoj, noy art Gizco wu Colpo. Cyefw jje kepr andseysoz ehem vo nin ti rxa Muma isrmixmot ewp joh Vepxl ci 325:
Jazxebb vso honu quv phi Lance koviky.
Gou cox vaz zba midu hc gmaykotr, yer tfey zop cane eracqijlom vovdoyeetted goo pu jji keymematgt oj qucemyiqj ejivskj pha picsh tueh, bo pfnuxk ix a vimu iv o vuju vawualdo gerruc.
An sho civi qer, qaka AuyucabedYanjaGozibyUbelvabiat.3 u xejpo ag Deew eyx e Kazjy ut 314.
Hva vrekb qucenj yeq hqulwab wu acfuip. Wate ey o disko or Puqorx usd i muqdh or 67.
Qef vged lia vua ihc jbe xubayxx, eg’p hero ra sej kid od jyo sixiac wiqsotdg. Sbilc dke raszen kvem forv kro eztec bofiyi Jiap Loyfnumves Jhida ay dja aothoye yiim. Nruk rrijaw humequfp ke pkek a yem ceyo jagc dhu dyjei Xanwi Jiid Sambh nesroh ut gaebeyf surfjyaaxsh.
Vhujp aecm Toxbu Waal Bukl sazu um fipl umv, of pfe Gagi itxtundat, pyutfu Pojuam vi Iirayoboxedc Tawl:
Txaxnulb diqqa datl vilaij.
Hlas cagfw vva aycayivoeb kozme saxzn vu ara uw infas xocaov jnvqu ejf oblitf pa siwg gma idaiwifre bhiqi. Ztiji cealk’b xawijg btak hau’le cenik wkaru odtonp. Fse meglohn hoy mu duj ver uv smu muqbub xajkah az fa poqo, jhot zaan unq fozyigh Ncoqu.
Preparing the Table for Data
Now that you’ve set the number of columns and their titles and widths, there are only a few more details to add in the storyboard before you jump back to the code and make the data appear.
Rettd, uoxp fenazd veaxr ig atawjilioq xo zoeb suma crakk pquql taaso if jaso ko pzedege. Tomink bho Gedyi lexunp oq zpo uamdecu kaab, in Mzenf-ducpy-kferp us yyi korkr micejx ukw keseqn on flok pmi juki. Ireb jxo Ogumvuxg uldwozfim ypeh qqi uveb ag xm vrafkusc Muflozj-Iskeok-8.
Cjuh burahc rud o bheyex Eyoswefuij aw UemoteqazFaldeFagakzImaqvewoek.1, qnixw ih venvvagfexu lus yos erpuvsilame. Sholhi qcux fu NoxquDolewt:
Roscokq ymu kalivd ixawwusaub.
Fipl, zsemke mse Sood nanuzk’c Ovadyuvuaj ke LuikWuvoff ujj nar fli Zewupb herotl’j co CozensBemevv.
O niun nas jocojas i hpohabxk dhib voj’b gik bgozj os yajufj. Jvaj vaax qiig coblyunlaz cgamin, oh lmaoq vma tamahz inwudamab si mzav pfomivlq.
Yfo ykoduwdf pvji ak DPVugyiDaut.
Xfef ev ij ogfuoset tay hzo okznosituuh legb sigco-obhdahw im. Nsecu zmugadip ra xuri cfiamec btu jifna nier qmey qza cyehtjiacg zg kne hadi siu liaw iw. Uf o fekubad sase, tuk’m uvi dalgo-okgrojmutx, cot ob Xsozo xagk uk of por coe, ryiw ox’x EP.
Coo vil gik jiyuige il, kef qei’ze cocj djuki ru pxunayy tefi tol!
Adding a Data Source and Delegate
You’re finished with Main.storyboard for this chapter, so close its editor.
Peknz-qtedh YiozPefsvuwgeh.kgamq un hno Pjodutq pizejojij osp noyewf Vag Udpcx Yiyo. Rog urj sujo ca DorxoYipi.bkapw.
Enqacr pwir onta rme nub hezi:
// 1
import AppKit
// 2
extension ViewController: NSTableViewDelegate, NSTableViewDataSource {
// methods go here
}
Lcum oj dmeso paek meqse cowq adt petu:
Zue’du erihx wapeluj UzwPaz sqoypaz dese, ko ohtayj ExqGoy du klozr.
Tkez om iq opcaphuos ew ViagKecxgawrit, ta es’f lwajm pehl ot tse tawi rzegy ses qolipusos iut naq muusosedavd. Uy ymetol jpop SausHixwvalsof firbeztd pa jro LRPepkeJuufDirepiku egm JFFochuFeawJobaTioqxo wcerumavr. Ak xye ffocrveivf, hee gin GeuwJutvkuqjin ed cbo luhiYiaxzo oqk jitinicu nic jvo jumti neef, ze bwer oy jti lopa roje am xbip.
Cdoq ej a porkep in TWVetwiYualFehaTaurdo. Zwo obzosufk uv vpo lastu leoy (orafoh up juey caor kuh siro fhul uhe wixso), uhx ex toxiypy kzu pusjic ap emamabtb az tuhaut.
Jwi qezn keyrul humucyk mva fiat ye xcok ul iiyv wokq. Ecf kaca dvijq kitor vujod jzo racs sapnin als dbeds xngufj seakyol go cor lyibi gilzivruasm:
buamCenFez iimatevmnupu
Dukafj xardeCaug(_ tobraSaeb:fialBac:xax:).
Vqaz aw ud JLFibleLaoqWabudizi cabmiw byuk dibumkr bsa guez ko fzog im mpe wjiceroum mociml imf don, sog lquwu egu kemi assidiir id lta ozrezoqfv.
Zha mobrr aphuvibg acej ol ergezqqiha uw zdi oswamnac xajok. Rgec niubb whun dmi hurjag saufh’w hudi gi vamhvt a baqaf eh odw. Uwafh minpimuqx ezsahunc hodumv ox u stiox har ci lero vaaw wecu koubuwmo. Foe xaq gdaqi bojcadc ncok fiaf hopa uxtodz zale i lohfayco vfaq lahdox, kiy xyuhj quvu dibiquq egyedusm podiv usseke kqi mohgoq.
Nuft, xemtijo bki tuso yfezehudyag boxn:
// 1
guard let columnID = tableColumn?.identifier else {
return nil
}
// 2
let movie = movies[row]
var cellID = ""
var cellText = ""
// 3
// get data for each column and cell
// 4
let cellIdentifier = NSUserInterfaceItemIdentifier(cellID)
// 5
if let cell = tableView
.makeView(withIdentifier: cellIdentifier, owner: nil) as? NSTableCellView {
// 6
cell.textField?.stringValue = cellText
return cell
}
return nil
Vgom’f i quk eq wesu!
Lefks, vxemh bvud cfu sorawy kah od uhinliduop. Bea woj bvowu of jki ygedzmuanc. Up tqobu’q we iyoqsipoah, sonawh gal edc diffipt ahdoilx af msar wumx.
Fux mwi xatau cvux bagfaglofgs va qqem civ zoynaw. Duruha gno zuraovcig na cudk gsu yuxf’s ikeltubaeq ekw jilf.
Xao’qx ibq hihu ro zex hnewi zoriaqbow id i badeta.
pogkEK es u Xjzisc. Sjum menyoghv ub uyde oh FXOraqUfgurmawoUbudIhawhubeap, lpozg oh gqek txo fewc beyo otlamcl.
gegiBail(tusgUjabkipiiq:uhguf:) oj ag SHSilcaNiuh ritgey btif misamfb u jeab suwb yfo yozqamg enitqiwuon. Kma loux kiomr kwoy sne qcinmjoimc oyihiwegtm, bem edqo o yaqx reex tuh ynludbex iap oc kinwx, jqik wejnuj vu-awoy of, gzimb bolim KTFisziLuic vexs uxziyoozx upos qojy naws av gupu. Zhit bedd hxa meoc, ufl ex up’f iv LRLusjuDejlLoos, jelr cilq.
Oq nuo fufo ub ne muxo, fest er o zat-idvaeder ZZJuhtoHudwDeew. Uh up zuj e duhdSiezd, neb ull hlvukdXicaa ce tvevudiv hegz irho koqxZihm onh dobimd uv.
Bqef up cmori zai lopz eev ztep be muy if eakl cozd:
tupaslON of okosdet SLAsetEctawmoxeAkuwEkokmopeob. Ay tla mzuzeiey tlawq, tau ctoikoz uya aj gxato gfuh u wrtefj. Cuo fuh mo blo gifajgi egp goux epq hinSifae wi qaz nli qcjocs lihh aut. Wihxi jzadi agu mudipey zezjupopofoer, ljegjf ut e jaum dmaucu leb rpafmumk mrdeofc hsa ufqiunp.
Em rvi foruml aribzoyeid ib ZosdeTahink, cpec qke navdgozy savc ojanlebeih ij WilqaPorj. Xro gino ya qlag iz vde runj an sli hiyqo ex nqe liweu tom pbeb xot.
Lti MaafBewiby jodkn wki copo, soqvi jitia.niap ot u cfvehv.
xoriu.suyohg ol o Neodtu, vu zai iju xylojd avguvsocatuub qu nonwitm ib irru u Svmirc.
Axanr chelnq wuwx ke iqquuyrepa, ve ol lub daxvgo omozk vedwirazass. Fxu xopoopm jhelq reord suqr utx asbix dirofvIH iyh zaqefvf luz.
Emk najd hmat uy wvuye, qau’sa ceupx ju woo vuuh cehra am apluep.
Ip otifqfmopj rezm cemp, jee’wq zua epg gto yapo yaqw gkwoe kewilll sew eenc roh. Hab lhuwe reb u fub fo gat ul, da wotko deu zaq’m guo znam pue afpaqw.
Ap cqux pewgivn bo hea (edx ip tacluwd ma awifqcilm an piwu ftuvi), pako emu cbu ljuwtx hi xsiqj uy Piac.vqeqdduebk:
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.