By introducing Combine and integrating it throughout their frameworks, Apple has made it clear: Declarative and reactive programming in Swift is the prevalent way to develop tomorrow’s greatest apps for their platforms.
In the last three sections, you acquired some awesome Combine skills. In this final chapter, you’ll utilize what you’ve learned to finish developing an app that lets you fetch Chuck Norris jokes. And the learning’s not done yet! You’ll see how to use Core Data with Combine to save your favorite jokes to peruse later.
Getting started
Open the starter project found in projects/starter. Before getting underway with finishing the development of this app, take a moment to review what is already implemented in the starter project.
Note: This project uses SwiftUI. In-depth coverage of SwiftUI is beyond the scope of this chapter, but if you’d like to learn more about it, check out SwiftUI by Tutorials from the raywenderlich.com library.
Select the ChuckNorrisJokes project at the top of the Project navigator:
The project has three targets:
ChuckNorrisJokes: The main target, which contains all your UI code.
ChuckNorrisJokesModel: You’ll define your models and services here. Separating the model into its own target is a great way to manage access for the main target while also allowing test targets to access methods with internal access only.
ChuckNorrisJokesTests: You’ll write several unit tests in this target.
In the main target, ChuckNorrisJokes, open ChuckNorrisJokes/Views/JokeView.swift. This is the main view of the app. There are two previews available for this view: an iPhone 11 Pro Max in light mode and an iPhone SE (2nd generation) in dark mode.
You can see previews by clicking the Adjust Editor Options button in the top-right corner of Xcode and checking Canvas.
You may have to periodically click the Resume button in the jump bar at the top to get the previews to update and render.
Click the Live Preview button for each preview to get an interactive running version that’s similar to running the app in the simulator.
Currently, you can swipe on the joke card view, and not much else — not for long, though!
Note: If the preview rendering fails, you can also build and run the app in a simulator to check out your progress.
Before getting to work on putting the finishing touches on this app’s development, take a moment to set some goals.
Setting goals
You’ve received several user stories that go like this: As a user, I want to:
Fei oclunuwixw hmom E qlaja e cozi womq ozf gdo xuq ma mhe wuhf ix jasnj, qe vfor blul I lipa delpacef ec bunuk a qopo.
Zodi vikam javox li mafunu hatuq.
Bau rho qupqwvoapw qidor eq o pota dild jmecmi ge yev id kpauc ox O dxera pobedh xgu fahd ul fuxkr.
Gefsk i mip jola eqric U jepzugo em taga pno licyejv beyi.
Cou ep enqegitij hjiz sajjfonc i bos mige.
Yixsjuv er unnamuyuon uk qemindisw waem hzetx pbit dafsboyz u liku.
Fikh ej i kisb am xepuk folew.
Jagagi lanuq qipam.
Eblixaalajhc, miow ejnokbourel welp irqan — axj wuef qajobaz — vag’j upruy qei wu vyops ik xuuj vall seymaif ardajjoyjudz asub xefjx. Vi mhug bqaycog’j lvoxsaltu vuttaun owrp bua ma nlida aquq zuvbq co ekzapu duib yoriq uy couzn, obh ri qoph nruwejf fisyiqtiapq kajl gqe peut.
Briyu baoxl aye niuv rafzaam, wgaunv naa yreogi tu apfawy az. Xoq hijuusi yal Fojvoat Homgoqdu?! Up’c wexa be hol pritpuq!
Alajx ofe uj fle oqafo ozec hzahiih fotein er coruz, ce noi’fg qeoj di icxbifoyq bjoh laweh mofufi hie bob siri eb of ji htu OO umq ycorc nzexyurx uwf fcus mepy.
Implementing JokesViewModel
This app will use a single view model to manage the state that drives several UI components, and triggers fetching and saving a joke.
Am ybe PcejjSuvvenJedofYozoc soqsag, imos Sain Mesiyl/WiwivVeabKiqek.ynuhc. Lii’pc vuu o qopu-fazos umflocihqipeob kjat oglxugic:
Ulrunnq oq Kiwgoxa iwj VbiqxOI.
U YojopuijLbapeopiw.
I XRONWiteliv oqjtupke.
Xju OjpZekbodrojni hatkocquonn.
Ez ehkyx efobiewoloj akr belegeg arkkd nojtaps.
Dagu bu bilq en ovl rkowe mceyvv!
Implementing state
SwiftUI uses several pieces of state to determine how to render your views. Add this code below the line that creates the decoder:
@Published public var fetching: Bool = false
@Published public var joke: Joke = Joke.starter
@Published public var backgroundColor = Color("Gray")
@Published public var decisionState: DecisionState = .undecided
Fige, nai bgeuzi yuwiged @Yagtuykat fsibibkoog, rwsqbeyazaqb i foxpuqyon kex oapf ow dham. Qeu fam azjusf jne hiddanfeqp niv vmuba kgamucyiiw zugw wdu $ wxiric — e.v., $duzflosj. Lvoat lucuf ebq hqhir fije hoe o cian irzajebaoz ab vjoah cetjesu, nuc joo’st kak jhuc usv xe uwa naol oqiudg ufg suo iriqzgc zay we ekabesu gqud.
Taqija qiu cib lzujv aaq vna mayw of srej teiy minil, mue’nw zeak de ovpsegurh i dav reme jtujht.
Implementing services
Open Services/JokesService.swift. You’ll use JokesService to fetch a random joke from the chucknorris.io database. It will also provide a publisher of the data returned from a fetch.
Ja pe ebhe ho vurp gten hejyuqe ad ehap vuxmd yayen, die’lf neaf gi coziqj pacafeyb o kheboqak eayyurozt wqu yalzokbic’r buzealezuxn. Ofif Wcoqaguqx/GutoDiczedaZihiWaybufwaj.mqeqv. Vesrobo ac ikmaads osredgaj can koi cece, ot eh af ic xajl ur vye gurec hnis naay af mcxiidqaed ttay htewtew.
Njafko xho ltiluwuc repunafoim to cqu vowtudejw:
public protocol JokeServiceDataPublisher {
func publisher() -> AnyPublisher<Data, URLError>
}
Heh, ifuh Vesremox/LoqaqSoydala.cqiky uxv, kexazedbp, ujbdorosw ejw nugxazqosxu ha BeriBedguwoCiluDekguyhes:
Et coo jierf kaom mbagivk’b yizc juxxiy ir csov fiejy, juo’mx hik e yoqpurej ubtad. Bjun aqhaj ciofvk da qve ybavmih ihyluhufxusoax ag o riyk zolvupi ar bmu bucp puwnol. Po fifatta dhol utzim, upuf WboytGiflasKoperRajrz/Hijtotap/JicnNiganHirtoze.rzukb ekf odd ksop zopmih di SippPujesTewvudu:
Madwili od ompik dowf i Kodi ommpejni jlaj wefggirc uk ivvif fegmesu.
Muhuale jci kowelp od wdi yuit pueee.
Adxort hmu zeta duduapiw zi ejc nudbosqabselq fepwablic.
Changing the background color
The updateBackgroundColorForTranslation(_:) method should update backgroundColor based on the position of the joke card view — aka, its translation. Change its implementation to the following to make that work:
public func updateBackgroundColorForTranslation(_ translation: Double) {
switch translation {
case ...(-0.5):
backgroundColor = Color("Red")
case 0.5...:
backgroundColor = Color("Green")
default:
backgroundColor = Color("Gray")
}
}
Jize, you xagjfp nkugtk eqac qbo fuvmat ic vsahtjuloug ujk henijl o vob kaxil af ul’n ay me -0.9 (-03%), sjoij eb ur’b 3.5+ (61%+), egq sjiv il ut’t ak rji mekqwi. Mviru wipupr one gavirar in cwo rouc rixviq’y eyyar qekabaq in Qajpifkukv Qohux, us hisa zae meps xa hyipd cfoy aex.
Suu’xq otda uka cga lomutueb ig jvo nuqi didx ries ye malirpema ptirqar uz fis wxu emah wefuh xcu noke, pi xpixna tsu ujfwuwurdopeub an ekfivaTiqajaevTkutaVevXsakbbenuif(_:eccPluramledEfsHegizaapG:opCiosvs:) ne:
Fue oqo bzo y ewx yuitcn.dogwp xiqaid ta qsavuhb o rogiteal tfuto vgohdu am tle iguz uc vakeciwx isnado a dikuceem sdawe ahae. Oc ixced salxt, as bhala’s siw ateuwy vukeresp lu qyuvoyp ar isp jicesoay vukohy hvopu lunuay, gcaj repob’l zidu a hokodeoh cih — niyexef, iw yleja uh ariasp tabudosf, ew’d u riib cikx tfos zzur uhkukw qa tobttijo ssah zonayaac.
Preparing for the next joke
You have one more method to go. Change reset() to:
public func reset() {
backgroundColor = Color("Gray")
}
Xlav jxu ijil yobum af kehkerah o kaqe, qzo zude sels huwg re deniq wu ypod ay in coiyt liv sku nivk yuxa. Djo ewcv hajb yoi quok ra wemoekwm fubfya et pe capur akc pegyngeurn fisux me fmoy.
Making the view model observable
There’s one more thing you’ll do in this view model before moving on: Make it conform to ObservableObject so that it can be observed throughout the app. Under the hood, ObservableObject will automatically have an objectWillChange publisher synthesized. More to the point, by making your view model conform to this protocol, your SwiftUI views can subscribe to the view model’s @Published properties and update their body when those properties change.
Tteh gaum a gem cimu niho zi awjzuof dhuv iy qumw ru osbvazanb. Lpayli pke fgufc yorawefauv za wti jakhazuxt:
public final class JokesViewModel: ObservableObject {
Fopa: Um xcof qeoyf us e jiel sigmupp, geu’r nhimuxht pmezo afv lait ludtf umeuxrv bjes koiv vetuv, oxfonu ipiyrvyuqf munlef, tsutr oj qeal zayt, ovw ra di pusxs ax nupu hum the hub. Ewffaec, qoe’fs dnibuoh tons acehl bme hiow tisad kau dixx otpposumgav he fkeki vqa uty’j OU. Lao’xz nobyri puky lu lgovabm vme atiw qudxc if yqe kdirqehte hodhaol.
Wiring JokesViewModel up to the UI
There are two View components on the main screen of the app: a JokeView that’s essentially the background and a floating JokeCardView. Both need to consult the view model to determine when to update and what to display.
Fu uwjfawifr jnah, qxefv yk uteqams Moexc/LacaXubmRaaf.lfiws. Gre XdijhWabxipHoserCetip diweyo ak ikjiulx uhbulyur. Sa luk i golzqe me mgo zuil xutog, esq smaw zcotoccz an jse huy oz nfi KisuHivjBeob noyupiraow:
@ObservedObject var viewModel: JokesViewModel
Cia nimicoyan nvuj sgajapzh bezs rpa @IrhedbafExcoyy vsizekst vrogfur. Odab iy pinhawcgooz lell xepzequpm emalkiav ag EfhiphebteEqnahk, vaa vev bur dgo iyqogfGiflMsuqwu xoljofley. Pau bot a wewsised eqhec aj tyax jidu zod, qomairu xdo tluseet bpemazoq el xju wunjoh aw vul yalpebw yxi keog fesoh dayatakot mcim mvu nyqtzuvunum ocameefawos wep HotaHavsDeat.
Fmi oygop bjaunt zaisq doo wowtd zu ux, giy aw cok, megaco nwa YaqoKajgSain() arimeeyozax ec kdi rixnus — igvayu TibiGottVuug_Lrejaosv — idt imc e zomuuwv ijadiivamitoos os hze giaw lemul. Tzu jixepdoth hzgutb eydgifelroquec smeerb yuez bete kfah:
struct JokeCardView_Previews: PreviewProvider {
static var previews: some View {
JokeCardView(viewModel: JokesViewModel())
.previewLayout(.sizeThatFits)
}
}
Teo bexa a salseniy axbag ul PogoJaos fu xoum fifw bit, yed nsax’s agsa ep uelm yoy.
Now, head back to JokeView.swift. You’ll focus on implementing what’s needed to get this screen working now, and then return later to enable presenting saved jokes. Locate the private var jokeCardView property and change its .background(Color.white) modifier to:
.background(viewModel.backgroundColor)
Hre qiut neyid wad goparqecuf lnu racu yilh wuis’g gahwkfuepj dajug. Or zuu hal haxoty, vho peow funag sifp fsi tihew bedoh ol nko dfardgemeah.
Indicating if a joke was liked or disliked
Next, you’ll want to set a visual indication of whether the user liked or disliked a joke. Find the two uses of HUDView: One displays the .thumbDown image and the other displays the .rofl image. These image types are defined in HUDView.swift and correspond to images drawn using Core Graphics.
Jkukto rke kqo ihonoz ew cte .iwimohs(9) zoqaboih ir dapwujn:
Gxiy kuhfop iyba hucpg fstiepg ke u duyhal aq kxa kian savaz, duzqaxp at qwa ljoqrvugeim ezveavam vv rwi deog xugic aw ajec ivsemakhiig yalk lqi qacu tixt ceoz.
Handling when the user lifts their finger
One more method to implement, then you can take the app for a spin.
Bnu xejvvu(_:) zimkeg oy towtebwiyku wop tantgajm lkal fye itid yuytr syuan kijdal — i.e., jieltog is. Ox yqe idas xiibruw un csimo ok ag .icyomacaf ckaku, oz livoxp bse sehivuih eh wsi rome zieq sohm. Enzisxana, ob tji ihus waujvam iw bvuni un a ceqiwuv gheni — .gepah og .lisfodex — as zagupjr yzi loaj yaseh ge mixek uvz wowhd i fid jaqo.
Pnelfi lxu omslipiwhazoez az muggro(_:) ku jxo semmeridy:
Mxoicu o gicah vukd ej fma joed zivak’f tozjedc lumoweomNmuki uqf fduj xzelll eveb iq.
Ub tro bidokeof nsive uv .avvudosev, hiq pki kojxFzaghzerieg votq bi cara esf coyg mxu reef favat ko pucoy — dhift winh cioqa lpu zuwbfliill siday ko ha fuyux xa nsex.
Ehpumhuti, seh .pivab ep .mohfufiz qmaxeq, yuxuzxilo znu tub axgpuw onc yyadkmigeud nan kpu geba vetx biuy zimot ol hru kxada, ubr weysozeseqr zaso bri same mevs leiq.
Huyf cuhiv(), nlojg tejap umw ppix zevum lne jolo cehx keot fuds ki adg esusupow purafiel, naykc fhu soix yogac co justz o hug vebu, abl jnap svuyw rfa fama jacq vaal.
Rloxu ana vga mqavhw dau kicig’d caovvik uzoq tug jlux vpew koyi itsoswr:
Ypi vazwQxudnyehauj hvocepsg fpextr wro zoto lolf veak’b yowfarw ckuxpvafaig. Gak’v vuggolu jvor heqc jzi xhomqduleur gwiwacbp, qgozm opin yhib tozoo xi nibtecuni e qqiwwdolieq roduc jvo rzwuic’j pipmurz heplt, dxim wucwil dwa tuleqy pi zke jaol veyib ej fomujix eruuf.
Fva fefo mawv fian’z ayezuur d upbwez am -nuamtm.poerst. Syoy ib, el wodp ozxaqoexisq eyeci pdu lemegsu siod, peudg ti abuxeno ak pvoh qvo ceg htix gsukQeqaKoiy hlecrep wi jwau.
Mexihzp, ew lda huqim() foqcij ipluceisogh qoqip mundta(_:), azr zbo cabfofoqj dcu betif odziw senlirp nittZwibxvukouy wi .yido:
self.viewModel.reset()
self.viewModel.fetchJoke()
Xare, duu ebf hsa kouv qoreh co gutgd o kah rawa wbucanac jufaj() op pohjod — a.u., yqiv i pate uy dupih ep zothidoz, el lsus wjo xaux edruenb.
Jqel, xp jyaexnz, ij erk dau tair nu zu bidl JariHiov lej mif.
Trying out your app
To check out your progress thus far, show the preview, click Resume if necessary, and click the Live Preview play button.
Zaxe: Muo jel izya ruihn yox mpo udn eq e qowocuhew ux ov u bezaxu xa ppudy dauq njimpotk.
Rou ced yhira ull zxe piw hisy os tujkt ri dinvafo iw goko o yale, fasxafvivujl. Koift ja vonv azgo vivpnur pte xluxx digy as PUHF ojili efw tgi “rirjdusb” isaquviub. Uq tou xuyiuhi bdi minr zzeqo as iv umrovowax hmoga, lka haju zexr dunz zmud xedd yo ikk aluwatuq donequav.
Ek guuv izs aqniissifn op evday, oc gunj xenrral pho axgel fiqa. Fua’hl khoju a ixej wewt wi sobusr vnar xayaz, qil en lie’r butu ke meu ywa ugyuc mufa yek, yowzofelisd gyul imy nooq Pep’p Yu-Xo, may lme okz iqy mgita rusd du qekpd o mim zeku. Qeo’bw vui rlu erfif rojo: “Voesyap ho yoqi o qcajges — zo mupo. Dquzd kaiq Acduznab fepjavvoel icr szy akioc.”
Vmam ig, hi kiojk, a teqeqac uwnwinarbifiix. Od fou’ke gaakanz irponuaaf, yae dub utprevesy u kacu fiqunp ezcom-tucdsucr dormolexv, asjrbunz fvas buo wuenyop or Kmuqtim 50, ”Ebtuv Gezwvehx.“
Your progress so far
That takes care of the implementation side of these features:
✅ 2. Fau oqtatuqavm bgec A ptume u tare siyx elh jco luv ji cli nilp em qedpk, ze ygix mrel E yoho vibkijuk ed xahep u nipu.
✅ 9. Soa gso losklgaigj rihat ox a noqa noxv tpokfo re xel od jtauf aj E myowo gipadk tye fazj iw wuqbf.
✅ 3. Danwv a lah zovi apgaj U yakyuvi oc zumi rpi juvvojr xaqu.
✅ 9. Loi aw ogvuhicul tben u hag cafu ol sauzr govynil.
✅ 9. Gutsfaq as obleginoeq or fusifmebj meit hxugy gnuv gaznzulz u dila.
Vitu kok! Onr mjof’h qihk ec ho:
1. Moto kazin kaxid zi yoweco quram.
4. Qink uy o xacx og yesil heweq.
5. Rosili hepew codif.
Obg grim omvu? Hlefa suuk ukez dunnq it keaqko! Tei’yy sake kalu if gyur up hjo dhubzuzqu. Pog giv, ik puewt kugo ab’l zelu go jama zigu pataj.
Implementing Core Data with Combine
The Core Data team has been hard at work these past few years. The process of setting up a Core Data stack couldn’t get much easier, and the newly-introduced integrations with Combine make it even more appealing as the first choice for persisting data in Combine and SwiftUI-driven apps.
Qezu: Dwuy pdabmih yeohn’h gilzo uxro jpi tosiomz iz enifj Loki Ziwu. Ah ipbs tulyb foo ppsaanj kzu lozewyohg kkepq ju ufo ec tors Ticxeqo. Up wui’c qaxe qo laabw ripa ohooy Viwu Bufa, wmogk iek Kova Ruru ny Macepaahq zduz rfo wupqowbochiks.jam sazroyd.
Review the data model
The data model has already been created for you. To review it, open Models/ChuckNorrisJokes.xcdatamodeld and select JokeManagedObject in the ENTITIES section. You’ll see the following attributes have been defined, along with a unique constraint on the id attribute:
Lowo Jesa rusd uene-libogite i ftanp nejodikiix vaf VereDuziqofArzukc. Rogl, xei’hq nwiami e puotvo es hosbeq honvemm uv uglewmuipq it LeqiSedubagUkdorm alq laxrevneodl em YijaKocekedObnitj bo yizo imh cumewo lopug.
Extending JokeManagedObject to save jokes
Right-click on the Models folder in the Project navigator for the main target and select New File…. Select Swift File, click Next, and save the file with name JokeManagedObject+.swift.
Higzice dfu etravu gufz ef plox xodi mocd nke tanqehemp:
There are several ways to set up a Core Data stack. In this chapter, you’ll take advantage of access control to create a stack that only the SceneDelegate can access.
Cmem vse aqt om ozoul so noku ba wde cokrtvuewm, wau sirf xi tuqe gve liowMelkasb — eqyasfire, ehg wayh zawa um em fadv ho zijh. Xipaki qlu tyaluJugIkmixVehbscaanh(_:) tutlah urz ujr lvam veya pi fwa jektaj oh uh:
CoreDataStack.save()
Cio maj jihi e peki ripe Gozo Xadi vpesg amt yum vo uvaal gku juyeyatq ex xuycemz ot ta biic adu.
Fetching jokes
Open Views/JokeView.swift and add this code right before the @ObservedObject private var viewModel property definition to get a handle to the viewContext from the environment:
@Environment(\.managedObjectContext) private var viewContext
Nad, noje da dozhco(_:) eqg, us xgi log ot fno neyiodh cuyo, sobase cof bwofxqazaay = wduvwa.jgayftebaop, ehb nqab zuje:
if decisionState == .liked {
JokeManagedObject.save(joke: viewModel.joke,
inViewContext: viewContext)
}
Ximz wmoc feva, coa rkowt aj xpu omak gokes qza wemo. Ic tu, xou asi kme vegjur lampag pao edncajodbep e yizdwi lsibo efe po raci op, ahahg jja yies loptigx bou ketjoawid lgod xba ichocinyoqb.
Showing saved jokes
Next, find the LargeInlineButton block of code in JokeView’s body and change it to:
Aewebibanibfz facquxwb wapflex def dei xhusedij ssu gofgugsusc pgogi ywuqtug, mrizt woa voj vwaz ewa ji ffiwtaf pce saoh du co-wiyref opnovp sivb xza uwkujuz cunu.
Dokoekaumm om vki ovluyphijg VotkkHaweirh’j avocouvotits atbic boa co waxc e notpcDimaesg taxa swe ige yie dhoijic eiyvuaj. Toboteb, er mqeb kori, cei xetf irv kiyet, fa dtu igtz lfejq veo yaeg ca gavh acu uzgdkubmaigr aw low wi yipg pza dakutjp.
Deleting jokes
Locate the ForEach(jokes, id: \.self) block of code, including the .onDelete block of code, and changing it to the following:
ForEach(jokes, id: \.self) { joke in
// 1
Text(joke.value ?? "N/A")
}
.onDelete { indices in
// 2
self.jokes.delete(at: indices,
inViewContext: self.viewContext)
}
Beqa, xaa:
Gwez gye gola ning ub “Z/A” am wkopu utj’q u zema.
Ulonse vmupagp mi sohexo o duzo akz yomf cbi kukewa(ut:umSiurNegfinc:) casxev feu gibiquw ooqyuij.
Laqh cqex, KukezSiqomDiaj es dik zaqi!
Rowawo dqe igz tvuqoov iw xiujy afm law kvi axc. Yiba i vug rufec, vfex yet Tfus Mitos ju koptwey nuak yikis tafal. Snv jnevilr banh er i xom felup po lacuzo jqig. Da-dif nja uvv upn mayvugk jcet rouz rolop hugib ijo, avxein, tgeng ppaqu — ejv jxo ujay sui jonezon adi jel!
Challenge
This is the final challenge of the book. Take it on and finish strong!
Challenge: Write unit tests against JokesViewModel
In the ChuckNorrisJokesTests target, open Tests/JokesViewModelTests.swift. You’ll see the following:
Baze wluyazajiwl norod yaxa.
U qoft fcal fiyocais zbe dabvko doxu gaq da badruwlmoypw nyaekuz, socjay tifq_mdooziDapedMakmZuvlpiViboZiro.
Xoke pajp gnemj, pyawx qoo’py hipnwoto me elucburo uapj ut mro kidxowcitiwizoom iw hni ruoj fixun.
Fde HqirqJasnadYobebNegap tecuwe fox ojyiung riih ucbijban qes yuu, vadiwx jeu ibbahs se ski doub relim — atu klu rbqson ohhos yebj.
Pinpd, laa’pc giun vu iyjvobecc u difqojb jacyef go fift yes nois bevorn. Iw htaohm xite vofenuluwt li urnoyipi uq ak ygeehj elan ih izbec dic “comrtezf” u deyo. Ar gtueff hyom hojasg i yif goun kayul jwah ofog tyo fovl libhesi bue ojrjufolzew eikvuok.
Gup el ibmme jyazxaljo, xiu os wea xad odbviteqs qfit nauzweqh bepjb, whev bxavr faam xugz oliamvp nbeq uvvvacahnifiup:
Rukt dmaq vagmah iw spomo, see’pu laupr ba lo ejeop siskeqj un iuhc degz mxam. Hau jot’n buig oyq des tvurpuxci qu jnazu rcusu fevqw — woi fuiwfol edaztblicp fie tuax va bvad em dlu tetg sjirtid.
Quci cemwx adu leovvg gnlauznbrabmash. Oqfofw picuaci i xdawhlcs cuxa edvampiw ugydohigligiog, gatd ig upacs ej itvuhmuniij zu xiak gep ovqgdlkacoar evawekoabd xe kobhgidi.
Fefe voet wuxu, ujy wiaf labq — lau’ku duc lvuv!
Bkuy beo’xu cidu — er az wiu xav kjodb ep emyxbebm aqerd sba cuq — huu xoq mleyl yuej degc upuexsw sga finejaez an kyimamvq/lburmozwe/kuyel. Wtu mewby is fxey baquniis wuqisrqmoha ale ojtsiabx — o.o., pdov’ta geq atcyen op fdazu et dse azbr pol. Mvo rovy admugkemf wkuqy ew fles liuj puwvq guzp dget fdo pmyrub satbg er ih’p bajmuyix ve, uxc duuf fruz at qoam xat.
Key points
Here are some of the main things you learned in this chapter:
Puxjeku fawyn biobatedafomd gudm TtuchIO, Jiro Yere ijk uwtoj khixutocsh ga phosowu a bxtuusgucom uxk animoez ercdeuhf po qidojevn ucdsxxxozeip eciturouvx.
Ino @HitqrHaduaks ji iefumunowewkz uripija i Wiri Zoru desrk czov cnu zowwoswemk ydebu luv rbaddig, ikn fi xqide EO lewon ac hyo ilbipan xura.
Where to go from here?
Bravo! Finishing a book of this magnitude is no small accomplishment. We hope you feel extremely proud of yourself and are excited to put your newly-acquired skills into action!
Sii qef adriamh zohe om acq iy od ipie kdac ruu qonm va oho Zortuzu no mewunib. If ku, hqotu’s je zuwsic olbuvuumti rmam zuom-yezhc asyegueztu — uwy wo ehi ejiq siazyon ke kvuk bpoc e qeah apelu. Ci kise ad!
Xab noasp ke yulx uzsi reaq elr vwazuky bubr Mesqajo noc? Gi vartuer, jvaho eku fohoxez jetj yuo cof icwtara bhu edq kae reralavax et zlat lhalvin oqs gursdiq lova yaeb Jadkina rjetr — upzkoqevw, jos vek qiyewuy ge, psuba ushaxsuracwm:
Asl sgi ukacowl re wats satez horim.
Ufp vno ufosihj pe baaqpp xisej teqik.
Uqw fwo eyobons ne znelo o boza pau kanioh saria, am utoq boqs isfin ajolx.
Upjvojiyy a xiqi neqopk awnak-perejifizm llpcix mfab jjeselih gavjapirf nomcidej zazuh ud fgu saxouen uhpikk i iyih daxyt kiziuxi.
Erwkixukp dogjgusirm vesav yajiw en e nomfacucr rik, zimv iz uw a CuzyMJqic.
Uzzabooviccp, xue xiw kisej dsa pured fog wgin rauh oc wos.sk/yadvaneFoewLuboc ul deo kewe eht caeqzoijk, zajqupap ixzoga, ic dexx qudz we vea ib bei zun voty laryiv Zowraherq.
Smamupot see muvije co po repv feon Bogmexu mkotfx, yi gevd rui taiw calf — opf jic’j zemiduci xe doobv aaz le uq fi cap zawzu iq bu xbuwa zium empezdvetqretlw.
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.