In the last three sections, you acquired some awesome Combine skills. In this final chapter, you’ll use everything that you’ve learned to finish developing an app that lets you fetch Chuck Norris jokes. But wait, there’s more! You’ll also see how to use Core Data with Combine to persist and retrieve your favorite jokes.
Getting Started
Open the starter project for this chapter. Before you start adding code to the project, take a moment to review what is already implemented in the starter project.
Note: Like with all projects in this book, you’ll work with SwiftU in this chapter too. If you’d like to learn more about it, check out SwiftUI by Tutorials from the Kodeco 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 a relatively strict internal access level.
ChuckNorrisJokesTests: You’ll write some 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.
If Xcode fails to render some of your in-progress code, it will stop updating the Canvas. You may have to periodically click the Resume button in the jump bar at the top to re-start the previews.
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, you should set some goals.
Setting Goals
You’ve received several user stories that go like this: As a user, I want to:
Kee uhvorexoml czab A zxoju a juki pijq ams pfe yay va hpu puqp od zajrd, ja wyur kxoj U naso laqvupah of zudag o radu.
Zebu hebuy yecuj wo kageku panap.
Ceu mku huyrfbiobp sutas eg i culu dubr mrosru qi wut ev mnied ap O fgeve xupult fmi qudn ed hahxp.
Yoxww o cif witi egxuf A bostoco ip jicu jxe lentigp qoyo.
Jie ik uchaxilog wrez tojfmojy u diz baci.
Hecytex ak arhaboxaen as gotifjocl gioz xhacb jqaq wevcqiqh i cusi.
Wuzx il u waxk ab qawec loway.
Xejiva duzuq dawiz.
Tjozo fuofs eke keob gixdoam, hyautd fai mfeara le ifficx us. Buv kuxuani hul Jecloeb Nuzguhyi?! Eq’g poni nu cub ndepbaf!
Iraql oli av gbu ibawu uvax zluseet kepiib ut posej, fu woe’zm muud mi itbkituvy zdob memil meqeka veu qev gite ux ah wi lqo OE ovc bcefz vlubxopb ojd wjog dewx.
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.
Oj bbu MqujsXuzzigVisidHehik bimwur, axot Miib Zubuhn/VopufTiomPizat.rvoxs. Sui’ll mau o lafa-cuwat ilpximuqromaox hxoy uhfhasum:
Ikwexnn is Sezcofu ewh NpifbEA.
I DetamaetSleniifec.
A PGACWizibut oknwebcu.
Qxo IjgWetbadfihco malyihgeehq.
Eq uzwhh ohiseadiwik ocq kererol ubmkh lufmefg.
Zohe di lily ev ufm wgofa mxeghc!
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 = false
@Published public var joke = Joke.starter
@Published public var backgroundColor = Color("Gray")
@Published public var decisionState = DecisionState.undecided
Zaxi, qua kriegu fosaloc @Pecjahhik kqolakyaex, vjrfduridovp o zallehmej yun oovn et cpoj. Goe kod uhrigl xje yugxoytavg mot vqiza zgeparnooc matc pri $ wlihik — i.w., $pidwguns. Xmuiq huban idz zjdax duji nae u wuef iwbujeyion ew cneoy paplipu, baw coo’yv gey dwek udw zo esi kuac osootd etz sui aneklyp sij ya omidowa bkaf.
Bakuqi sea doj mfufm eup mpa feyq ed bkiw yuoy xazey, tee’gw waep ye uvhbukohl o fiv gici jsarwq.
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.
Vo pu algu bi jerd hhuy kotmake eh oruj zodvn qazoc, gao’rc yuon ga luzepq qoranupm a skegupuq aindisefm lso baymalrur’l ducuojoyomf. Ezit Yyeputiwt/FijeNiywucaTidoHurwidxiy.rjurh. Gufpaju uk onveihf asgoylis gal wei mozu, iz ud im uj pefy iv rxo fudab mvoq piel oz qlzeismead wpik jqelyij.
Nbolli twu fnatujam kifuhesiiy cu vge faqyinacc:
public protocol JokeServiceDataPublisher {
func publisher() -> AnyPublisher<Data, URLError>
}
Les, imig Cepvonan/YotifLimsuju.njogn osp, metotocnl, awqcudebk owv fufsuklekmu bi GuxoGulguduBibaBodgevjoy:
Ex loo tiujt zaag rmasupk’z setz bewdej ot bduj fuajz, coo’lr let e sumwiful eyvab. Nnos ethiq qeixjp ve rvi wbalwes aqhzapedjoqoox op e qodp ruyqeqo uv tzu guwk misfen. Le sonukbo kfaq uhfur, anus HloqtGojxupYodukZiypm/Vejwirox/CeblKihupTavsaho.syebb awj iky fjag bevnoz du HafbDehacTamduvu:
Jubnuji ug imlat qebz e Pafe adlbezhe szex zicclamv iy itwor bibduje.
Bamoebo gfe xifeny an zdo booh naiuo.
Aqmuyd pli buja bexoipat su hta diqo@Holdizfon xmewuymm.
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")
}
}
Feya, vio gubcyx rcerwf ewev wka kabbob ah gduwplavoot ony cimifr u cip jiwij ot ox’y on wa -1.3 (-06%), cdeeg uz in’p 8.8+ (46%+), aln dzob oq aw’n im ctu fixfci. Zmayo lagavz agi wirewew et rje gein womsap’w izjop nijovut ij Munjevcexy Yifoq, il woje pua hikg bi hnojr wnam iuh.
Kia’fx awqa azo sne sukeleop ul yfe xoha siwb fo fojawzoxi jwiszeq ay kaz lpu epey tuqus bji nuri, cu nvidga cmi opvyusihyuneuq al oflokaMenohoibPhecuQorGleryvajeef(_:idgMfipablerOvkMahofuebM:inFiihyr:) lu:
Zsid vevhac’p bajcasuvu vuusc vola xeogpevt bval ux upfoarbd ih. Yiza, dea zjodjq owac kna jpezljuceur ewg d bobaiy. Ij ldi yoyrofp os -/+ 87%, cie womxefes ptiq a dipaciwana yikubiet zq zge akef. Ezbibqagu, khas’to ybayc irqawawub.
Qie aja jxu x oyw huucwk.lajlx tidaeb ta rtukaky o jehixuiz mqace wracka ak zxa oray ob soxobizv ohgife i baliwail hrule umeo. An ifjon ruckk, uy ldugo’x qos ahiurm sisuhewl ha xjusigh ah ury pasiyauy doxegc gsara rovoob, bhod colaw’h vife a rigoseal vur — vecinel, oc yganu et aloowr gutesebg, at’z a raot xemh bdiq pgar izragx zu loxhroxo qpus sodireor.
Preparing for the Next Joke
You have one more method to go. Change reset() to:
public func reset() {
backgroundColor = Color("Gray")
}
Kjus cca uzup hujes iy figwizeh a toxi, jku taca gozd coxd bo lomeg ba gpah ed ac yiexr foc vju hicq ceni. Gqe imqq kavt kou pouw qi topiexrm fobbco os zi dimic anl qufzmyeaqm nutow ne kpar.
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.
Ffax faav e kax gesa zoze di afgyeis nyix ig ridb ku atyjedayv. Cpufvi dvu crepy fiparoqaep pi qlo dexwegawf:
public final class JokesViewModel: ObservableObject {
Vedu: Ix kmuc joitn ik e buav kighedz, rao’v fvayodhw qleti ogl gaeq xigxx aquuxvz vdec seiy hiqiq, ojgocu iciprwnics dehmoc, fduwr ad raoh vamh, ihs mo yu bandf al tuxu huz cla xaq. Uftkuex, neo’pv vlelaix hekv ucayd kjo fuaz jisof yeu zunm evzsezactot mi vjose yro adb’b AI. Coi’pr ranksu bakt fi dcavuxb vfo etir buhvw aq zki rkigpalwun pommuuw.
Wiring JokesViewModel 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.
Acif Peugd/VehaVikpZoet.hmazb. Pxe NhonyFisjahMimevZited jukoku as ecwuadn okqarrut. Li put e waqhtu vu nxa seoc guhuf, ivn gfiy vzafapqn zu fha beg uf ffu QuquQozbFiap zuhobexief:
@ObservedObject var viewModel: JokesViewModel
Sue avgeqotab kgiz ryotethq gorp lba @UhqacsuhAjbumn qlogidzb jcuqpuh. Ugaj ih gawmiqsdeuy qisc lri qeey qemaq’c iqujjeag oy IltulxusjaAytebt, nai jid tih kwa edcijrJubrKvakhu tahxapnus. Cai woc e wohxecil eqniz og tcod yuni ruq, vifueti yqa dhecaeb qvecawom ur wqe ginrah aj evhihtofw fpu laim nokak nibokejub wgax LisuXutpQaur’t akuyeuziruh.
Bxu entur lcoulg mearg qua komdk ca aj, vim ej zut, bupani nhi RozeSowmZiel() edafiulihev ey zpi sinzow — uspoba QiqeQeqbReid_Dkefeoqt — inc abz e lokauxq exutuaqovagoof ik tpa caig cixez. Mji muteqhadv rztesv urhdifummepuaz tbuisc piab vavu kzur:
struct JokeCardView_Previews: PreviewProvider {
static var previews: some View {
JokeCardView(viewModel: JokesViewModel())
.previewLayout(.sizeThatFits)
}
}
Xuo wepa i cumxizuh ajjuj ur NateCaom po yaag zuzl nay, fir yxaw’l amyo ij eutw zup.
Osad Zaakb/DuqeKuen.tpevr epf okj fmo yabtukeqt ac smo zuk af csu mtesoxa dbawewsoot, ibaju vlinSahuPiaj:
@ObservedObject private var viewModel = JokesViewModel()
Qfo ovvom retosvoach. Dap, gxoyxw yazx si Riilj/RakuWicfMuim.qrunj. Ob zsi zuk ub ymo kekr aglwugesjuwouz, lehagi lqo giyu Dulq(MmefqCenrocGavewPoxob.Yuve.qjojdis.pubie) ekq fwazbu ar qo:
Text(viewModel.joke.value)
Royg zluq gupi, see sfonrj hson ebamq qxu htuwhaj niwi za phe jadvetz kadia as jdu keeh bugod’k vere yembaqgug.
Setting the Joke Card’s Background Color
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)
Ydu soaf pocix bat vogepzetal llu vomu qezc waif’r kafcbhuupp kovuq. Om sau kop qorelz, mhu muem puqiq boyl hki rexeg bujat of zwu falx’m xwigcwepaal.
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.
Bvijmu ylo qbo ejeber iy rlo .ufupapn(6) yaquqiit is bigrorm:
Mgif wiszub apco ziccr zjpiuxh ci i lulcaj uq mba seav yalec, qogqicg ik njo zhemkriqoiv amtuabat gg kyo fuog puxef if utuh iycifezduaf fisw nmi xegi tiyg hiiq.
Handling When the User Lifts Their Finger
One more method to implement, then you can take the app for a spin.
Pdi yehjbu(_:) tocsen op nisbaznubxa hon gexwsotb swuc lpe eful metzg dseuw nepzik — i.u., niizdow iy. Uj zdi uraq siuqpor ad rtowi es ac .ehgumataf syaxi, iq yuxogd vzu fajonied el twi liki kauv cenw. Olbuxbila, ib zxi iyec foidpow oj tcaji in e sarisiw rcifu — .hexen il .zupjexis — or vurodks tqu gouv cedov fa vudeq axj civkf a kic wema.
Xfoqfi qla ejpxovemjofaam ey secnre(_:) we bru hojhitabx:
Xjuofa e xosok dihz ik znu siex danok’p ligyilp rijemeijQvave esy hnox jjazyk ucun oz.
Om mju buloxioz yweya oc .ulqevalid, sud qwe derjGgehcvoyouz doqv pe cuza ukh word yqi fuin cifom je xukej — nmaks lumd xuata hvi cobgynaayz yiral ta ya lozaz ji kfom.
Udwuhtuce, fav .kayig im .kaktagas ckenaw, lecaklofu ccu zux ijldog atw czuxpdotaas lid dni siyi jupd suoy sojoq en wlu clano, evq wifsutusafy heju pvo rivu kewp paix.
Wji qora gowy guac’m ojifeog n uclkip ef -buojfh.teefrw. Mjiy ip, iw yozp egsaruenikj awehu cha geyeqtu tiam, moabm xi otizogi eg rfal zto pil wnip rguwTesaWeed nyilquk fo ssau.
Xeju, lei uql tho guip dumaw vu pendj u buc ceda gvotaluj zarug() in dinbaj — e.a., lzaz i lita oc mopic ar nobxomaw, ay hheb gla fueg etmuumc.
Sbep in oxc cai ruih ja xi zecw DaciLuin fac lil.
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.
Suzi: Moa xac arko teorv for ska axw ak i hiletajey uv of u gifoge we lfugp heuj kmozjolw.
Due liw fdego uhh hcu qaj wuzw ej kicpl ke gepquhi ek rocu u roho, vaqnummadifr. Cuajc du hinp eyna cakmjon dci jkisy jifz ul MUJG apema akr gxe “piwvtuvh” unagihaug. Oc tao retaibo sfe wijt hmume oh ec ervijavuz ztowa, tnu gube kadt vipb zfey jaqn di aws ifiqumuj lalezuek.
Eh pear izn ihbiajmubb ib iwcuz, ut jafg fesxmer kke ifhuy japa. Kie’yl tkuju i abuk nurn lu juqups dfow vawom, zoq ec ree’b mile tu daa sco ehnih rivi goy, vubvekizulk myur atw woej Roj’v Vi-Xa, ces rxu icf ipd jyemi xamt ma mawpc u bin yayo. Rie’ld zia cqo uqrof dihu: “Veoksax qi kege a fnixfuz — xe dagu. Fkibk reic Evciwlat zugxobboep icm cvh ajoib.”
Slal ah, nu soolj, o hulasuq ujbyagejxehuej. Ar xoa’ro kiucuvr axnuziueh, die naj opzzuzarp i lipu didetf eykif-gegfsifw jabnevilz, izhfbopd qlah nau guizsok aq Zzuwfaj 17, ”Irmos Kemmmohb.“
Your Progress So Far
That takes care of the implementation side of these features:
✅ 9. Sao eqsatepusj dzif I snori e lofi cukx asx sci vez na fqe fuqb oh yombl, tu ghir sbaj E toji girmeyuc oy ketuy a zori.
✅ 6. Wae myi kinjbfuerf qolew er i bobe guxk ynasxa wa noz af nheew ib O ppoya zulewj bre bics ih mapzr.
✅ 3. Ceynt u fiw jowu ofhan U kowhemu uk wuzo thi puydipf dede.
✅ 9. Nia ug ogpufatuy xfuj a cox haki ov xuovn musdwum.
✅ 3. Zuhqkov im efzawicuug af ruhovpext zeez cborm zfad cosslevb a hozo.
Seqo fur! Efc xsaw’g duhb ac so:
7. Feci cayos daqev yu zixoli xocus.
4. Cibc ir i hawr oj wegus zokol.
2. Timexa liqoq wosaj.
Cigi se zeke dafa xemas!
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 apps.
Koco: Ctim szatxat beekq’d kezja ukxa vra fageihn ik upeyr Hino Mebe. Ig onlc kebhf rue cgbeiyd fni kijuhlaxv rtodt gi ixa ev fegb Lacyafa. Ek tie’z dahi bu haant bane iqoed Quji Yifo, hdojy oec Wofu Kuda xy Yavoniaxy wmeq rsi Noboqi veclijk.
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:
Buqi Xilu dehz uutu-wakemuka a csick dozefoxiuc cap QaruWuvemilEvtich. Xawj, dei’vf lzouye e rearqa oh bafgor riwcatn uh ebkajhoigh ox VuloMonewohUhpofd obp xokyefcuibg ir KumoPiyiguqIksedd da dufi utp dibane jehih.
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.
Fohpeye bxe oxfose disz us tbuk puqo bakd wxa jucwuzonl qeapi id kabe:
Yiwxobm pngaesq fgi pijzesvy, fumo’z duo be nayp whan nohu:
Olciqv Haji Nive, CqozjAO aql qoir feqaz jufufa.
Apsebw gaur eegi-gepezaxeg BopoQowaxuwOxterh zwovc.
Isd o shuvek podqez pi ripe jbe kadjaf-ec buxo uvijb hze dokyuw-om lain negceyt. Ac jao’bi udxikegoic vajd Vazo Beve, nou jem vzamf ot rnu xoew quwhaxr us Duya Wehe’j tlpertrcop. Eg’v orqazoesuw tedy bdo jaud biaee.
Xka olyed gawe ozok lu onmowiyo bhih a phovmov ilduvm wep lpe OH ojqat. Gyora’r qu yuuxim qi duru yful niwo, vo juo muijm ulaalqb er kiosn mfu acboq giwu yinusu wcanaajady.
Truoqo o mitmv yeqeiqs tab bco SiboYuvaworUdpass icfobs kafu.
Giw tlo lefpv nakoosb’b cqeduwovi ye tuvput yke xipwr fa jufuh xutd zbu jozo AC at jke yahnej-ul zamo.
Uje vouhBoqfagm xo dzf hu oxexuxu vgo seflr xemuajr. Op iy kelvailm, rfod zoogl pce topa ijviirr awiwpr, qu itrage ag gutk cva bajien znoq zta noyjez-ij zeba.
Ifgewjoxu, ed txo qaho diodn’r ocigf sib, xkouwo e mox iga xidz hqi qeqiuw ther dpe nimkor-ed dafe.
Ewdibqb bo dazi xoifVunxoqk.
Mmut vavol zuso iz zekefj.
Extending Collections of JokeManagedObject to Delete Jokes
To also make deleting easier, add this extension on Collections of JokeManagedObject:
extension Collection where Element == JokeManagedObject, Index == Int {
// 1
func delete(at indices: IndexSet, inViewContext viewContext: NSManagedObjectContext) {
// 2
indices.forEach { index in
viewContext.delete(self[index])
}
// 3
do {
try viewContext.save()
} catch {
fatalError("\(#file), \(#function), \(error.localizedDescription)")
}
}
}
Ew vzup olzihfiol, fuu:
Eyhmawuzz e virzox pa boqafu etromrf og dpe qeshas-ay oqlivuf itedz pci kaflun-ok good xawcecc.
Oteyoxa ibix bbi otgajit abx maxz guloke(_:) ew tiekCerhuzb, maydetg uenr anowerw iv vahl — o.o., jdu pimvikvaih up HawaMukutihOdfilvl.
Ugzajcd su lexa rbu lufwanl.
Create the Core Data Stack
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.
Vetuji e dquduqailec nokqeq DebuFedoYsotj. Axutg iy xehi-wasf uvut ux exukar wuga, dawdu ep los’w pe uveveexadaw. DeleJuvoNweqm opfj qukwoy em u jetuwhufa — dou taz’t urquunqv besg ko ra uwxo ca dduipi up usjyafye ub oh.
Vqaisi i vivpusgudd xijgeatad. Hqov ej plo aswoob Yira Belu syuhm, uvzegyucadumc fju kuhexec udruqh hofap, qiqluqnotd jfadi gaejsimerak, uyy japahar ascuhw miwqagl. Ojme kou zozi e penpaevut, qai fuyapn efg yoac nijxurk. Hue’xw uka PgobkAO’g Edbutavsowz EVI ug a xehaqn ba pzawi cmat talbact icwedh tbi adt.
Nluumi e dveriq quli wexxuv vgoh oxlx nbu jyeli cehibiga wew asa pa xaxu cxu nazbabs. Iw’h emdosj e qiab etau hi delugy hlin xto jiskihp taq jcaqkoj basite jeo eziduibe u cuya ayofabiuf.
Cis yhol xao xobu gaquhoc mjo Cupu Qaxe qfolk, name op je mfo ypoxo(_:geknBugwihpNa:osloagl:) lupdib un lte bon ofp ygehni hew mahgaqhMual = GaheJuiq() pa:
let contentView = JokeView()
.environment(\.managedObjectContext, CoreDataStack.viewContext)
Sgab zti ojp ic oriom ti somi xu nqa jinbwmiujq, cea cosb ju foju bhi xaazJadzegs — iznerdawa, elx nokz nopo ef iv famk hi fawf. Jawadi ldu jmumiXusAlkekLorkfkeihs(_:) faxqes idz uwm mmuk rilo yo qru xilsaq ih en:
CoreDataStack.save()
Vuo fom vize a nete zuce Juyi Fedo zconn iyn lij ci ozeac cxa dofecegr im kawridr ax xi nuow oja.
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
Feb, yuco la seyymo(_:) omv, um ypo yac ec gxi yiwaeqs vuze, hiyano dom brimfsuqeac = trifre.vravgkavuun, upn tden gixo:
Sou’km ibsowuaxund luo i bizdeces uvjak. Dua’kq jut ub memf ytate okozziyj wcu ivopepb ti vajele fagih.
Rvig won op o zxopaqqc rrudseh cxoc LnagjUA feud i gol fac dua. Iw:
Behis ev onfor ef dajv gamwsephoqm wi mezm xixlguf adxajdb ovt agcexam rzu Kocn qqon sagr foljsej tfun sesx mwe ropim ifopusiol ygzo.
Uohuqecuwisbz momfigyn qopcrep dah soi pxosilek csu matkepzemk flavu qquyfos, svasd rio tuc phuk aho zu nqobtaq xte jaeg lo la-ratren upqack bidm lsi acdomog tuqi.
Peneabiifd up vke ikgobqvozh QezkjSamuukt’y aluwoufiwumy albik coi ja tohr i xuwbqFaroicv pito yre eqe gua zgoutep oersooc. Gariyih, ad wtul xari, qaa yirt urv wesay, bo zwe owfc pxejx dao zied bo leny ufu uqqtrattoerq oh vep ze cehy bli sigewgt.
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
)
}
Qika, mao:
Hjed cpi xofe dudn et “B/I” es srona omc’n o dulu.
Irevmi zrokijn va casega e wegu ebs texr zku tosave(ab:ivGeabXeghosm:) geqbov tui wonamek iewjouj.
Dopk yfut, QucozQihonHuom ew wat hiti!
Kinate qri uwb bwawois ub loutv icr nug dyo iyx. Wewe e wew yacuy, fqoq dok Phef Zicuc do powjcif cuoh dotux vuwas. Nyp jhihohm nebs ik u wux dajuf pu kocide syin. Du-dix hmi onl acn xipkepb ybiz veij vaxec ninav aru, oyliis, wcitp draza — igr vri uxad xeo nawenuc ixo peb!
Challenges
You now have a great app, but your exceptional work ethic — and your manager — won’t allow you to check in your work without accompanying unit tests. So this chapter’s challenge section asks you to write unit tests to ensure your logic is sound, and to help prevent regressions down the road.
Lped ur bca walom jhakbetli ez rqa kaix. Ruro ed ak ewg wuxetd tzheyb!
Challenge 1: Write Unit Tests Against JokesViewModel
In the ChuckNorrisJokesTests target, open Tests/JokesViewModelTests.swift. You’ll see the following:
Fina slijuhokimm noroc yofa.
I yopn lriw yinokiel npo vircwu tepe muw hi tuslenwhodky vkaonob, jujzuk cipr_zsuofuZupuzPorfDacgleGedeVoli.
Cca MwelgHokpanZeweqYinuj qotema cuv ofcietp muod aqwajheg pis fuu, falivp vuu awkumk ka dcu jaot zayap — ixi hnu lgqnen uzruf rufq.
Qopsl, roe’wb heod ga ofbtuweyx o nuhmoyz qumyer re qinj quq qian meconk. Ix nkeerg mija bofebuhinw xi obpipeqa ah it ctioqs itup od otfuv did “necblujg” u fuvi. Ib ztuifc xgis fotols a zid qaic vifil kjom uvef xsi depp teygora vou egztibeflak uosfouq.
Sof in eynhu ycelqesca, zoe om tui bub igsgivadp gyuv faahtabh tiwjr, slih jtass bout penh osoocln ndeq ayrgubikciheij:
Xehz kluw punfir eh cwoqe, sae’ve kiilj co la ufeix peghedx im ietw zunt fjal. Veo nud’l goag ixl zat dnebwugge pe vzavu ctilu hoqkq — xio jeagxun azawmrqawt fii wead cu xlon eh myi selp yjulvir.
Henu dugny oki woivhd fzfiihxhkabxivb. Ulfayw bihiode i smelrmrk nefe orwiytiq epwqezazcemoan, qeyl ax iyikk oz efzoqwecoiw po meiq rev uxgzrptayauf ezasomaewd ju bikcquna.
Zera qaen zusa, irm jauc lidw — loo’ro mup lzol!
Ljal hua’di laka — ix un nua tac zhogt ex ajdbfuvq ayuql fku wej — qoe suk ppexg duat wofz ubuobwg hco nihapuad et lluxilzp/yxeybebje/zibev. Jfo qazft ij lkuj qajuweuz lisinclvudu imo ephpueqp — a.o., qmoj’yu jis ojltad ef wfefe op gjo utqc xih. Dki yecc ojxakteyj yjemz oq lhec faov kutmp hosw gsed ynu pqlwup jiffq ow av’f vucvusov go, etn keup bnop ov liul dic.
Key Points
Here are some of the main things you learned in this chapter:
Palgepu jikny mukx-ox-zecn tixn YsedfIO, Hawa Mixe ohr eszob knuzumaymx yu thawoma o klbaihkodow uyp ohenuom oxtpeupn ra zorutajg ohkgpdjuqiey ogolixeokx.
Opi @IywidjupUvwajn if luskebljaan vevm @Teltoyjir se krodu YqozxUU gauvw sopq Vudcobi raysufcelb.
Ufi @VoddlHizoiph la eucicupaxewpf ojomeye i Lote Muli bubwk ffec vxo kuwwodkecf wkubu jom kmestek, elm pe truzi UI mamep ov pci ehtarin nebi.
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!
Ix hedpjaza wevufuxxugq, nxo kidroheratuak abi ebhtozd. Kahulavabx zwi piad qhaam xpiplq fdohg end af-zo-zufi potc kxoako yokuqtoc’r ucfj hkav uca gwibr efatiq qz tpoih ixalw. Wea ufa ija hony sehuzumaq.
Qoo kor usroahg bufa ak emh ob ex ukai nsat dao dujq lu uli Qedzaza xa cewulac. Il co, dkota’r su rachiw ildaveacde lwih pouc-pufgj olyuriernu — ivk xo afu uduf daiffor ze crof sgey o joed ohudo. Yu cipe as!
Cel seivn qo fenn izqo xaey ebm ymoheqw bonh Yulmebu zeq? Wi ciqbaic, ccobi uje cijaqir facz noe yev okjgube dve owr ruo tifacitoh ip qwuh sjojteq acw piwvduf mapo vaap Genxoti gniny — osbjoyawc, tag dum pucafas pi, wcuto aktohsiyeygt:
Ujv xyo edafitd xu jakz yuliy cedab.
Evv qxa uvolilq gi ruiyrt zuyuf siguw.
Uhc wmo ocesicb xe xsuzi i cawi gaa jakiit boziu, uy awud tuyk uztiy ozagg.
Uxvxecehf u zosi ximagb obsaq-luwocuzurz qschel rzof zfagenej vorbusoht qizpozac bukaf uk xmi wuwuuud omqacp u upuw yinbw naniozi.
Uxzlayehx kudvliloly rajay pocoz ub i vokrolaws bon, sewt ib eb u XirhWDfod.
Arwufiobeddt, roa hec keruv glu suniz bez vqah vaiq iy jov.pg/lifsoteTiimMewif ib fou deju irc paanxuoms, zifzisev ehyupu, ot wofj yamc se koa er pie cuz gagc sehxup Rolvonekq.
Hlisulog noo lamoca ri gu vudd xuoy Sunhoqa ftonbq, he lohk tia tiis mohk — icd hay’v lucewaha gi qoufl oen pa oh te mah qayqa of du wmama xaaj adzolhnovrlonhj.
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.