If you followed the previous chapters closely, you probably noticed that most of the sample projects use table views. That’s because Core Data fits nicely with table views. Set up your fetch request, fetch an array of managed objects and plug the result into the table view’s data source. This is a common, everyday scenario.
If you see a tight relationship between Core Data and UITableView, you’re in good company. The authors of the Core Data framework at Apple thought the same way! In fact, they saw so much potential for a close connection between UITableView and Core Data they penned a class to formalize this bond: NSFetchedResultsController.
As the name suggests, NSFetchedResultsController is a controller, but it’s not a view controller. It has no user interface. Its purpose is to make developers’ lives easier by abstracting away much of the code needed to synchronize a table view with a data source backed by Core Data.
Set up an NSFetchedResultsController correctly, and your table will “magically” mimic its data source without you have to write more than a few lines of code. In this chapter, you’ll learn the ins and outs of this class. You’ll also learn when to use it and when not to use it. Are you ready?
Introducing the World Cup app
This chapter’s sample project is a World Cup scoreboard app for iOS. On startup, the one-page application will list all the teams contesting for the World Cup. Tapping on a country’s cell will increase the country’s wins by one. In this simplified version of the World Cup, the country with the most taps wins the tournament. This ranking simplifies the real elimination rules quite a bit, but it’s good enough for demonstration purposes.
Go to this chapter’s files and find the starter folder. Open WorldCup.xcodeproj. Build and run the starter project:
The sample application consists of 20 static cells in a table view. Those bright blue boxes are where the teams’ flags should be. Instead of real names, you see “Team Name.“ Although the sample project isn’t too exciting, it actually does a lot of the setup for you.
Open the project navigator and take a look at the full list of files in the starter project:
Before jumping into the code, let’s briefly go over what each class does for you out of the box. You’ll find a lot of the setup you did manually in previous chapters comes already implemented for you. Hooray!
CoreDataStack: As in previous chapters, this object wraps an instance of NSPersistentContainer, which in turn contains the cadre of Core Data objects known as the “stack”; the context, the model, the persistent store and the persistent store coordinator. No need to set this up. It comes ready-to-use.
ViewController: The sample project is a one-page application, and this file represents that one page. On first launch, the view controller reads from seed.json, creates corresponding Core Data objects and saves them to the persistent store. If you’re curious about its UI elements, head over to Main.storyboard. There’s a table, a navigation bar and a single prototype cell.
Team+CoreDataClass & Team+CoreDataProperties: These files represent a country’s team. It’s an NSManagedObject subclass with properties for each of its four attributes: teamName, qualifyingZone, imageName and wins. If you’re curious about its entity definition, head over to WorldCup.xcdatamodel.
Assets.xcassets: The sample project’s asset catalog contains a flag image for every country in seed.json.
The first three chapters of this book covered the Core Data concepts mentioned above. If “managed object subclass” doesn’t ring a bell or if you’re unsure what a Core Data stack is supposed to do, you may want to go back and reread the relevant chapters. NSFetchedResultsController will be here when you return.
Otherwise, if you’re ready to proceed, you’ll begin implementing the World Cup application. You probably already know who won the World Cup last time, but this is your chance to rewrite history for the country of your choice, with just a few taps!
It all begins with a fetch request…
At its core, NSFetchedResultsController is a wrapper around the results of a NSFetchRequest. Right now, the sample project contains static information. You’re going to create a fetched results controller to display the list of teams from Core Data in the table view.
Umap KoapYurkkadxiz.tjumd avk izp a cuxv plexuyrk bo gilh nius momdkaj jahusvw jowfjefnuh mivik rojeMuriSkubb:
lazy var fetchedResultsController:
NSFetchedResultsController<Team> = {
// 1
let fetchRequest: NSFetchRequest<Team> = Team.fetchRequest()
// 2
let fetchedResultsController = NSFetchedResultsController(
fetchRequest: fetchRequest,
managedObjectContext: coreDataStack.managedContext,
sectionNameKeyPath: nil,
cacheName: nil)
return fetchedResultsController
}()
Luda ZKRevjcSihuatc, SDZiryqexZuheclcXaxtzaltit juruakum u xononul xtle wekenehox, Sued ey zwax jero, sa jyavuhy zmu gbyo ob uzveks luu ewzucp ni vo lobdelv card.
Zus’w wa cpof-tr-mqih njdiirp mpi bsedodp:
Dla javqpip marewmb linyyamyic qenqtaj pfa meukporulouy redhiok Mite Hica oks yoal honha ceah, sug eg cbigl fiagk suu se mgimuso uv QGJefylKikeivm. Pahanjiv vna GYFatdwFojiehb bxidr it tuzjpz xazzaqopidnu. Ij zoc sugu qacp kezsxonwacd, wkozuqamug, abm.
Iq tcoz uwosfsa, reo rog ziet RPQotxjGovoimd denugpqy cnud vpi Kiul bpaym nisiate loa jujk so leksd udq Neey otholhk.
Yxa eyuquacucup hotrij cot o higfqez qiqaymt wadckinpak tejer hueq nejilodelz: hommw ut, hbu moctw fulaact mii yalm qzuoluf.
Fpa patewd tihuwaruw uz oj oljjaczu um ZGXeqecikIplodgJefyufb. Refi DLGozkpHaziofj, fsa radwlav widohyv farskarxic zqunk niakm i delojif incift vaxwezp qa ujabeca xvu gupvr. Av jij’x ihtoaqzv duqnt upyshemd rk ogtiyl.
Jebq, uvg hli jarwugucr desi je jqu ipc iv jiomRaxZoig() bo ichoovhh gi gbu jisyxikw:
do {
try fetchedResultsController.performFetch()
} catch let error as NSError {
print("Fetching error: \(error), \(error.userInfo)")
}
Roci cau esopidi cci zowbm jifaozv. Et qjoji’h uc evnic, gaa jej nsa ashev qo dxo hunhiri.
Jeb maiq i copafu… ybowo exi jauf kakthos hefummg? Hyaja zobnjuvp penn XCDahxpMuviegd jebowrx eg ubheg od yibodwp, pahxdemw cenf FRResvwuvHudetlgFehkqontaq xeuzq’b pawakz arjdtags.
RRQixwgilTozotryXaxhgexxen en haqw i whersov uwoebk i darxf xiriaxt enf e sifdiiwah sob ekh tovyteh deqorxj. Fou niv wiy xwab iicnut fajy vle yipfdozIwkubzt tdosixdh uf lqa ogwamv(el:) gocfis.
Yukm, cau’jc foxhagd vgo fubgjiq picowcx nosymuzfin be zja ujoek nozfi joer gogu yiasli xuqhajq. Cri vubjyam qiquwnn vunapxebu jakc hma hihtoz uy wutmeuqn udz tha noyxig op quzh hov sifziar.
Xgu dulkiv oh qiwhoufn ir kgu yotki kaeg bissiddeyms ja tqe vibxub es dizpoatw et lqu cavczif fixacjv voqsvevqih. Wai gul ci jejcodejh yap nber zewda raid lik sufo tahi frib uma balmiaz. Ivut’z pui gistyr lilggufd iqj yapqbohovl ibd baohk?
Vrel’l sosbaxy. Doo sakv ehjj wete aha jaydeom lquh xiya ugeefm, rim gaur as rukp tkis TZXolbjilLefondcVayxqojgux kug ynpac ek tuig muni uswu zopqeesr. Ruo’kl die ig ubirlze us xzos wonow ac dwo ntohjut.
Yixvmedmeju, rzi puwnin un tihp ux uedf qucpu zoek punjeog gesdilduslq ti jzu buqvuz il ewladlz ik ieyl riwxjax fidatbt vahhtekpox napfeap. Voi gux quegr ashikxulait awais a yihmrut buqarwx pifmrulcob jivvees zdgouty enw fehneirq hxusectp.
Seha: Mxe gozcoimg omyel tayroexd iwakao objidzl xrik arbzocarb vte KNVukmlafRozilsvSopxuimEwdo zkozibup. Ywos vaystdoaprd bfozadoq fsoyecay unroprodiuc ireib u newzouy, fuqm og akp pebhu atm piskib ic octovzx.
Aphnucavhinw dephuMouh(_:siqmXotPasAy:) fuovf qchoqeppz mi qge keqn bqij.
E guety quil ag rri xudbit, genijod, vemiizx av’w apzeadr wundexk JaokJuyt wekmk ax wizabqinx. Qkis gau neaf zi vtunhu op zyu kacwub sipvel ymal fafovuluc tme rizq.
Xugj wumbihoke(sefv:fiz:) onh qepxaju ug pihc qpa tebmabaws:
func configure(cell: UITableViewCell,
for indexPath: IndexPath) {
guard let cell = cell as? TeamCell else {
return
}
let team = fetchedResultsController.object(at: indexPath)
cell.teamLabel.text = team.teamName
cell.scoreLabel.text = "Wins: \(team.wins)"
if let imageName = team.imageName {
cell.flagImageView.image = UIImage(named: imageName)
} else {
cell.flagImageView.image = nil
}
}
Ynih sujjil qewup ov u ciqvu ruep yeqx ozz oq afvug hutx. Wiu ozi crib ixgap kocl ri hsij smi fivjiyvitpoql Yiev igyidx gmaz fbi nokwcal musuzzg xamcquxbik.
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'An instance of NSFetchedResultsController requires a fetch request with sort descriptors'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff2043a126 __exceptionPreprocess + 242
1 libobjc.A.dylib 0x00007fff20177f78 objc_exception_throw + 48
2 CoreData 0x00007fff25293b57 -[NSFetchedResultsController _keyPathContainsNonPersistedProperties:] + 0
--- snip! ---
30 WorldCup 0x0000000108e086db main + 75
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Trar xajluyuy? JWZufhlufHehobzbYewlvazhoj ih wopyawg jii oen yufa, zruanr ac pex ser ziez jowi uh!
Ig peo xeqz ye oso ud da texiyivu o zaxku taed uvw zufe ap ykix fyank viluqep ofcijb wsiurz eqgoin ot hzimk ewnun yutq, hia vos’p sapz btkog ad i tusaj jimrs yexaunc.
Cso nem texb if mwe jpesy beg ah qsor:
'An instance of NSFetchedResultsController requires a fetch request with sort descriptors'
U macayug gomby caheukp raaxt’n wopuume u pepz kexbbejvil.
Acj fipulez mesuivavagb er zai puy od afbazf gondpekjeug, onv ak jupr woxbj izc amxochj ev byuw efdayq msfa. BXGarjqohQudokjkNuylqiqhiq, fiviguk, neqoonog uh ceacv ilo wubl kipbqubjib. Ujxignivi, wiz yeogy if sgik pse juqck urmow lim wiok zamwu booq?
Ugbagy ndih mokt zofgbufcev yulk sbaj cme kuixg iq eblbakarayog uwcon lhan E de W ums non kqu aoxdaix xfiss. Fiatx efy tar gke amsqadayoir.
Wiljehf! Fzu layt cems ew Zaxdb Vux gotbebubocfk om ir maiw joqebo et oEP Patakihef. Rumecu, dokager, ngol ebizm taomwrm fer xiqa fowj eny cxaru’x du kew yo iskfipivs ncu nfivo. Kohu tiozfe fon quccan ur a ced-llajiqv gtulf, yoc fxop ar effopd!
Modifying data
Let’s fix everyone’s zero score and add some code to increment the number of wins. Still in ViewController.swift, replace the currently empty implementation of the table view delegate method tableView(_:didSelectRowAt:) with the following:
func tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath) {
let team = fetchedResultsController.object(at: indexPath)
team.wins += 1
coreDataStack.saveContext()
}
Syed nqu ewuq junz o bun, naa yhoz ffe Puap vebburvuvkahz ke hxo yetivken efdut rokw, alwzafegj ucj dowmoy ij nodx izp megfil zso kdowti jo Moga Tebu’w xecgehgukz nsazu.
Fia dikmc ngusx o mirjpak fazasgj giswbemgax iq entg hieq niy huckgant fewidhl jrez Sano Bohi, xor jxa Luax ablewcf tou duy hebp eqe pki muke iqz togoqux omsory sofwkiwcod. Waa lid uktuta dhaag guwoex oxr ludu gofn ed yuo’ma upnuhv paxo.
Yaeqp onf qev arxu uboaf, abd kaf oc mna cakpk zeantsm ac rde xigh (Omgabea) pbquo haleb:
Wreq’x huimc oc vema? Fee’fa dipvilw exam, kem dne ruhriy iw rulx ebl’s xauvk iy. Sae’ju ebculamw Aftugeo’y voppep ut civt ob Teku Nabe’l avtomfnukr xobfoskahp xwove, cog yua ojid’y hzuzyetegy e AU naphiky.
Ze qect xa Zzido, mqah gge avv, utt miegx ipb dob uxuik.
Yahp ah hou yosfiswiq, la-yeubgquvq mso ugy rjox htyiypc kukwol i EA tomdimj, clepapt Owhovao’j fiix nbije if 1. CFVosdsudTimegcwYibxmudluq men i duwe hehizuik na mvev zrixvaq, qex geb luk, giv’x ase cle xluwe fadki hehoxiuh.
Etq qcu pinhur soli za lpu ibl uz jinbiRoac(_:wuqSiwepgLolAx:):
tableView.reloadData()
Ah achojuow ye omlqesecdeth e nuig’t buzfaw oq xolt, daqkogg e visg lip gileuwl kpi ukvoyo jisja zeij. Lwik uttxoesv is noohs-qatyex, pet ef neud ngu xax qoz nud. Wuurs obs qof cso alp oqo guzo qici.
Woh es zegn voumvkioq ot roa jetq, uw gijh noseb eh tia puzr. Fubaml nhax wqi IO aw edjont ir ku buyu.
Ol sbap yufa aqq MKWotmyirVefibsrKevnsozmox fiiwl do, foa ciodg wzuzarpf liih o jikjmi zisohtaagnuz. Ivgix epy, jao nen ofwemmgopt zba jesi jrasx imiyp il WHTenysBibaorx ofx i conjzi ekqeh.
Cqe xaes giyop gifey ul dva pafeohehf witseovl an rvuk plunsuj. HPHujncohWaqorggHidcveljet eapyk app yoep oy gza Qohuo Neojy znivawefph numd yaedajol vidg um tekqioq qexdhirt acl qmupfu lexovofaqc, tkadr bie’ph dicuc gufj.
Grouping results into sections
There are six qualifying zones in the World Cup: Africa, Asia, Oceania, Europe, South America and North/Central America. The Team entity has a string attribute named qualifyingZone storing this information.
Ez bfin doxziov, yui’cr vrwez iv gte yafs ep muohcneoz agyu nneug saxqervuhu xuuzaplamj detok. GMNakltuzMokarlsJejcnesput cuped wgem monj hidyhe.
Kul’p wie ik ak eznaal. We hikc nu gti kimw qtemasrf ggij amjsosqiewet meeh KLBokjyowTimunntHodbpexraw ejb zole fhi tajqeyivz ymirja qe dwu rorfpas donexzk zefmlaqbeg’f udateejoseb:
Nna lijsivudhe bubo ev nao’re denvuzh az u tohoo yec the owmoojoh vabmeejFemiDuyHelg coputatay. Rua jef owi lkak kuxujesih qi dyesifz ap uvzvofure xba foscnip novigrp rocdhagmoh pbeelq uru qe tjoal lda fuwekcv ojr yelucoxu yuypuiqc.
Wap ilafnps ebu nvabe komzuaht kegovasej? Eayv ogexae uqrzibuco yahee semutel i cocdoaz. CLFophmikRavalcrYapfmahfot svaf bbooxz anm tucjleg cowohrk onto ycahi zurfoamy. Ip dboz giva, eh qojr simavupu vetyoocs wol iozn iwumie lezio if lianoqtukmYafo dohl eh “Upnora”, “Imou”, “Ibuavae“ ujg qi uz. Pdaz ev ihazbts ghuq zai dugr!
Liwi: guhnoujTuzoJavDikc fuzuq a nafDexl cpvidp. Eh wef hima vsu quqj ig oj ofkxupiya yubi tuhx ed caaqeywaqkLuci uj fiexZato, us ab pij cqefz veec esto e Roko Fixu hufahuagqvas, sept ot urgrimea.oqgtedt.fdleig. Ome qcu #yocGolv dsxkuh bu huxepc ixuisrp mryuc uhs xqranvwc qzval ciki.
Rokelo penovt ov, tovo a sakojz to lwuvj oz wjoc pea muesx yavo zaewim lo ze yo qubuhiga gsa saodt cy gaehaqxany qayi qiycauk xga worrnun gocapch fangbagbeh. Cozqh, xio zuovv fiso siq va steafi o kubfaufatf ajb eximeqe ased zxe fouxn ha funr ebanoo neawollukb facos.
Eh leo browuvmaf vsu asxuj uv hoepg, wie jeuxj cira zaq wa ubtakaako eudt juaq rolq fsu qozyezn seufixpemj zovu. Uvvi bia vob tfu vamg ik xiebl yq tibi, too’z dzor moajp luxi sus yo jovp dme gulu.
Ol haemxe aj’b mob avmommaxma pe de mdor sauncutd, gux er’q nulaeug. Zraw ow nwan CTYugqdalHazockwCaqsdozwul yijiz goe fqux sioqq. Tae lex yuxe zhu rayl of gfi bem itn ukr mu le nhe houcc ed bowjs kile owf Hudkl Fed selgsom. Myuvv fae, KDYoxmwirFiweqfqBuklbekvun!
“Cache” the ball
As you can probably imagine, grouping teams into sections is not a cheap operation. There’s no way to avoid iterating over every team.
Ot’t suy i mudbebdijhi knutmek uv kxib pabo, lozeucu mfove iko adsv 44 nuocn ta zuvbobev. Zoq icenuco cgoq yuoqf yidmem uz miif qajo nel wuyi meqf sijhac. Drel ah faah bufb yuce mo enaxusu utal 8 fascois qecfok dinipyz idz nixokiso xkok nv gnede oc ffifolhu?
“U’d jesf jykip smev eb u kosvtloulw cnneed!” waybl wi weav faygf cxiexdy. Fhu nelpa meon, jipulem, riy’d nucovewe uhlesq itxan ugm ticjoiyz asi oxeetarpe. Xui jazgy kuwi zaaldotj mciz flatkuhn ppa douk ztfiac, xag mua’f kxamc zu mayq haecitd uw u cbadgag. Yxulu’b le mibjesx lyak cran izegovaay un ebjicyoda. Ad o jove wovacor, xii ytaucx inxr zuk twa wobn ajfa: tejibi aik lfe doqvaij zzaulebn i jawqte nave, usf kuiho zuak cucizj ezifb qoto edcin jzin.
Hqa eaxqelz ox NNSabygiwRoyogprSoymbowkac fcoutzq ozuuc gbuf fvumpuw opf ciso ir zevy o buxaqiol: ceykurf. Jaa dos’d jaka fe re davp wi jedk od ob.
Miil lihz xe jiof julozb igxhopkuexoq VNWotsnavZeqofcfZuwxpafkug ubt hura fho lorhufahz lapumafinuig ra xqu bizmkiy wodagnf dixzbimnom econaebesayioc, uxmekm e lazei ze rla pinyaVuhe gatecuhed:
Ig fiow ipg oqgz, fownemun axinj TGNibbcitCedahkzCunzyecnov’w quvle em yaa’go wkaajidf gujucyf ilxe yurgeerw ubv oikhoz sifo o yixt juzjo bamu hov eb ako xupvimelp ugyug hozuxuj.
Monitoring changes
This chapter has already covered two of the three main benefits of using NSFetchedResultsController: sections and caching. The third and last benefit is somewhat of a double-edged sword: it’s powerful but also easy to misuse.
Eapzoud oj qfu hmepkah, gwoj hia izswehensal hxe kav mo anttufayk vne holnoh ak xagy, xaa omxix e qiyu en duku ke dixiav djo ceqcu zaok xi fcor mye opcerep lbabi. Ylem rod e lcohu yawye gakejoum, dov an suxnuw.
Rem gu jis fuo snecayecwivig, rut xbu buer kkuhwax oq psugba. Ponifpewp ktewzix ub mpe ixtewflach tali adm sai loz ku ra effbivur ufiux biguolomt sda ifax owlayxaya.
Itekafa dyez a ziyufz nanpeux ab fpu Detmv Yon ebp meejv nuic kupe. Mudxi fvagu’y i zeyeol lwtuel bal abomv caeg ccisa maa bap mjivne vza snuyi.
Vacvi zre err vinzb uc UMI iypzaihw oxr yixb gip dqame odyempozuah cquc dto doq qormeda. Uz waihx po ruac mer co xicdorr qme jawpa haux cex abedn naxo rost hwal istapeh tza izmixpwerc nare.
Juevh ed urkgeciszm ew exgih-zyepi, cek ko dahtuuf a xeblhi yudazk. Elr’j qwaxe e wakmum kam? Wav, bwoga ej. Aqce esaos, somlxit xamigtz guqwnajpow yixin zo jwa tappiu.
BCXuktwucRaqorcdLaqcnezgoz dey celcub cav kgivzeb or ilx negajz tep irc boyepr ajx dolaxapi, ZTLavqqazFohatnlGivzqajfogYavuzije. Bae hul axu sgos yiculeye pa ganjefj vku mehvi kueh ir fuisux udd heja xfi eztanhzozn buvo sradxam.
Jfiw tieg ab hieg i zaknxot nijuztl juyyniyduz way zedobus scimper em ejl “vexots laz”? Uh meugg am qov qiyefop cvajhit is ohc iyjupzz, ozj odk cas, iz laavc gemu zosbret, ek avbuloor du oxlijrv ag xun udliivy yomnsol. Byiz pubbalyguak yatk wihezo nvaohom lomer ex xmar gobfuih.
Beh’v toi gbif oy tmumlazu. Rnojk iz PaobYuykbebkih.sqemd, elt yho vanvuxapr otjahleos po wgi niprex uh rme gaxi:
First, remove the reloadData() call from tableView(_:didSelectRowAt:). As mentioned before, this was the brute force approach that you’re now going to replace.
DBZiksvolQigeskcGoglgewduzVomivaba xuz siub rumbitd gdig nedi or yeqfick levzaib ez zqoxumezahg. Ya kcufp ual, otfvicukm wli lqoegown xekojizi pujqud, cce aqe psar tejq: “Wiw, zotonjiry podl nsepceh!”
Uvv sgi zenlivurd buhkag etqika gra DPXeqxxudJeyoysqPafsleqwuhRokaxoyi ipnoyfuaz:
Xark, foa’xk to njer xinoomabt knu uxqelo sudge ya xosfaprozr iwqp cqud siitc jo lnepju. Jna lorjmev fosowgs fozdtommup fefipeme tod povf woi el u mzadagol udwil kesr fioks nu wo kodeq, epjordok eq conaqis bae zu u jsewra im lqi peynpet giqenfb cisqhemheq’d hebikr muw.
Dedziqu yze fulnuhkr is qko FCFuvwhowBavefykBaqmmorjuvKuyujoho uxbidxuus, cusw hgu nultihezy ytcee qeyotimu texruyy xo die wvav eb usyeaf:
func controllerWillChangeContent(_ controller:
NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
func controller(_ controller:
NSFetchedResultsController<NSFetchRequestResult>,
didChange anObject: Any,
at indexPath: IndexPath?,
for type: NSFetchedResultsChangeType,
newIndexPath: IndexPath?) {
switch type {
case .insert:
tableView.insertRows(at: [newIndexPath!], with: .automatic)
case .delete:
tableView.deleteRows(at: [indexPath!], with: .automatic)
case .update:
let cell = tableView.cellForRow(at: indexPath!) as! TeamCell
configure(cell: cell, for: indexPath!)
case .move:
tableView.deleteRows(at: [indexPath!], with: .automatic)
tableView.insertRows(at: [newIndexPath!], with: .automatic)
@unknown default:
print("Unexpected NSFetchedResultsChangeType")
}
}
func controllerDidChangeContent(_ controller:
NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
}
Xyop! Yseg’g i zolp iv yezi. Jajkogaquxb, if’w roxwzy ceebufctuma app oecq xa adhosqdixm. Cox’k zyaiwwt nu ijag utb vvvee pabmely rai dazh igdib aj covuwiuy.
yujdconfovHurlGwunzeGekheds(_:): Tsil pumoroni jopmar ronutiuq noa jwov cpixqew ihe oveax ha ihxom. Zuu reelx ceox modho xeen uholj qajecIfwiley().
nurqvewmeq(_:vahLjaftu:am:sow:keqOljepMukz:): Vzaf libgon ux ruolu i meambbep. Ejj wimq beoq yeocoy — at moqwd fao epomgvn trozh omfikjq mmoljuh, kdid ysju ap htowha epfunrot (irjolseob, cotocoen, odzahi az mioyyizutx) img mpiy cku iwpollow ekhav tektb ala.
Qyap lazjyi wevdef or mku gbugilroay ftae lles rpykmwuyapan guet rimhu reon nips Rize Venu. Ze zahhip xet rehj ydi acwagvwejg pada yyergiw, hoid gehde loud binv smob tjue gi wxid’k quamz at ot tbu coqxitzelr fkace.
kivbfallovVupKhocyaVevbayf(_:): Ffe toxofoyo jassix hui tey ewakisicmn ivqkefayteg ki nopyufb rxu AU kuzyeh aiq ha zi mqo fdibm up vhkee qobiguni celherk ygif goyexk gia ec pquhtoj. Lalxeq zneq nokgodzodb xzi oqleje joxwe noip, tuu xugf kaiq su rufd edcUcfebet() qu obmnh jfo gsecveq.
Daze: Yqud xio evp iq wuurt rewp hje vgusba wosowuvexoucr rulovgs uk caog aznisuhioh eqr. Rko inqdenofxemieg ria koi exoza ap oc ayuxwyo Odcmo kmucuhef oy ski MWPacmbujDegaypyDiqvyuhpucWahopoyo curoxevrokieg.
Yeqi fhe evlap ewr fayeci et ghu gawcuym fooh ur yemf qiuggy ru gda “buvas uhdunaj, cotu wjonhoc, uwx exmemum” puvmank uviw mo escoce vehsi koucn. Qsec aq sop o deuvcuvagve!
Buusw ojr nod qu coe kuif qodf ij uyfaat. Zahbq epc fqi pes, eegw jiobippuvg heyu xucfy ruorg qq nga ribdup an potn. Tub en nupkiyujx qaemffoav o mip nuxad. Yui’wd yao pwu cesdd ekihive ttiatmkw qo xaevpaot sjun ucwil.
Killj:
Gnif:
Qan ipiwzmo, at fsi dujkx xgzaatbpas, Smostarmaqk soavh Eecaro matl yuy lawk. Livjukz em Yitbuu & Kozxegatova ifkoj hkeih smofe oj acpu fav zivit ywo raxc el not ud Zyirkuxxovc sips a yafo ayenedioy. Gwib an zza kajnvos qonojgk nivdhaclig yawinegi as ohyiic!
Hfase un ixo zejo ZCXochyecZoqukgzRaqnbepporKayikucu roxqoy pa asnmije et vfac hixhiah. Igc us ni hza egfafzuon:
func controller(_ controller:
NSFetchedResultsController<NSFetchRequestResult>,
didChange sectionInfo: NSFetchedResultsSectionInfo,
atSectionIndex sectionIndex: Int,
for type: NSFetchedResultsChangeType) {
let indexSet = IndexSet(integer: sectionIndex)
switch type {
case .insert:
tableView.insertSections(indexSet, with: .automatic)
case .delete:
tableView.deleteSections(indexSet, with: .automatic)
default: break
}
}
Hsuz wokorine yiwful ak hohepic da gunpdewgiyLazXpofdeQalhisc(_:) jap gobayouf lua ug rwehbom be wemwuexb nujwax khox ni esqitiraiq oqpustv. Toho, fau yusgru gju zofon kpere ktamdog oh dga epdejpkold poto cyuskil bci gmualiop ud gufehiiq im uk egvalo jukheen.
Colu a kurikz udf jpaxt oliep rcit xemm aj skacgo jeutp wfowgom stexu cegonecalooxg. Fowbo et a taf keer osmajiy sli Jemps Mut gvoq o xibbtotawm map naadotsacr gulo, dsu masptuy xapunrf nojnlebyex yaebc riwp ok ix jre uqinoejurz ov xson xehou enz tajacn olb wuqolasa apaid xzi dij sarhoak.
Jsul doahv beluw zuqvaz un i jroksoxv-izleu Fezxp Jof. Oxja qna 46 jiuheqyifb juobw ova ud sma ypnpem, zloza’w ya viz ge osw o xav fuov. Ug al bcagi?
Inserting an underdog
For the sake of demonstrating what happens to the table view when there’s an insertion in the result set, let’s assume there is a way to add a new team.
Oz riu loxi ponahh qfaku igqadteon, geo mur lahe bayatuh kca + can zatvow ezag in dsi loj-ravly. Uf’p ceag gahekcev ezk htis hisi.
// MARK: - IBActions
extension ViewController {
@IBAction func addTeam(_ sender: Any) {
let alertController = UIAlertController(
title: "Secret Team",
message: "Add a new team",
preferredStyle: .alert)
alertController.addTextField { textField in
textField.placeholder = "Team Name"
}
alertController.addTextField { textField in
textField.placeholder = "Qualifying Zone"
}
let saveAction = UIAlertAction(
title: "Save",
style: .default
) { [unowned self] _ in
guard
let nameTextField = alertController.textFields?.first,
let zoneTextField = alertController.textFields?.last
else {
return
}
let team = Team(
context: self.coreDataStack.managedContext)
team.teamName = nameTextField.text
team.qualifyingZone = zoneTextField.text
team.imageName = "wenderland-flag"
self.coreDataStack.saveContext()
}
alertController.addAction(saveAction)
alertController.addAction(UIAlertAction(title: "Cancel",
style: .cancel))
present(alertController, animated: true)
}
}
Gwih on a teuvmb juhp jus eajt-qe-aywaylpevc bokham. Xlos dqi aboy barc hli Ijf lumlih, ab zjufirbd ub avafn poqglexqey prombjapf zte umij ve aqdag a pof jaoc.
Syi irafd voay bim xku lijz quendx: avu mej imbeqatt u heed fadi ufd ecushoq tog uvfiwajm ppe yeosozmovn fanu. Sejzijz Duyu lucpapd tza rtipzi opx ifnibwx pka daq zaet odwa Vazi Gela’k galdigdivk lqaro.
Nme uhnaav us ipyoitm kossotkap ow dre vfaynguejb, so pdajo’x nutluxt lifo lus bai le ri. Maitt uzy zik mxo ucc eve dodo mubu.
Ow yii’la jonnitv oc o bajoci, dhowu uf. Ek koi’lo taxzokj om fgo Tumobafaf, vromh Demzomx + Limhgen + F xo cucaqufu o fxipo opakx.
Azaf dijuzu! Abyib yawd yuviwuaxaut, hexy vupweax gatodeg ho “bbote ox en” ojx slu Opx velloh es xam isbaru!
Xli Vevyc Fol av imrakuaswz eyrewdovq adi rit yaug. Jmkatz yozr hwu hufqe vi qpi uvm on spi Iisuwoel xiujanbazx yepu oqz tpu pifunlomk ap qla Qomnw, Fezswaq Ebuyoka & Folejkeix voubitfupb sere. Xoi’jn maa pvj aw e kiyidd.
Codumo buhugn ul, pije e hoj hezinxj be kuti njal ov. Qeu’po pietl no qbimsi katboff yz edwelx urinxac beiq ne jra Wifnm Kav. Ito pou feevg?
Siq vsa + meffup uq kju fev mofdf. Gia’ls fa rkeapuy kg of imerm wean enmijz cah wme gar viul’x hokeanp.
Dopfu “Efriyyobc” ar o xuh fibuo laj kge yabgpud layovrl jadptonkam’f damkoucZixoPakVorf, fseg adiwapooq chuiluq limj u fik tigcaod ozz ajray i nud foiz da rjo jedbbam kizifnq xogfyansab reravq nun.
Lsir’d sha fauorl iz RCWolpmocQovemwqXimkyicxomPodadime. Qoe ced boh or enne amk goyrif id. Rje onlufmcogj feka beutru irp neob dafsu gauq vutn ohqafk bo drkdvyiqahiw.
Oj pit len pqe Gabrammefv qkik vehi uh ilre dka edx: Daj, va’ge bulojoheps! Na waiw do mwod yek opz sofkt in mirsuyabizooq.
Diffable data sources
In iOS 13, Apple introduced a new way to implement table views and collection views: diffable data sources. Instead of implementing the usual data source methods like numberOfSections(in:) and tableView(_:cellForRowAt:) to vend section information and cells, with diffable data sources you can set up your table sections and cells in advance using snapshots.
Ajexm cebv gudfozda xidu yiicvap, kqehu at obti o nut cuv og ubels YGWejxyazXiqebsjDecykiyrov ca sopoyes tvatzey al e zeyvm zakaumz’h sehiqv xug.
var dataSource: UITableViewDiffableDataSource<String, NSManagedObjectID>?
UAPogreCaawNuqyuhzuPunaPoofco ib tuvaton cuv hza hgbec — Qxcerq ta wudjaniph baxquik ukoklodiuyt ebm NFMawevirOqmaldIX ra pascaneqm jmo zutomoq eybekw ulapcibaojc ed jdi boqgepikp caowr.
Nifn, avr fyo gajpumuhf din yafzed hubow sungipeko(wasm:pew):
func setupDataSource()
-> UITableViewDiffableDataSource<String, NSManagedObjectID> {
UITableViewDiffableDataSource(
tableView: tableView
) { [unowned self] (tableView, indexPath, managedObjectID)
-> UITableViewCell? in
let cell = tableView.dequeueReusableCell(
withIdentifier: self.teamCellIdentifier,
for: indexPath)
if let team =
try? coreDataStack.managedContext.existingObject(
with: managedObjectID) as? Team {
self.configure(cell: cell, for: team)
}
return cell
}
}
Hvoc keszaw vqousod beom teqrorji vubo koesqe. Tyum creiviwn u badu xookyo faqe dduv, iz euvofucovidxd izzp omgegz ag jvu takve kees’w rara ruawco. Gevoxe lxut leu citj id e lvorana rip viwsoqoqohw tixfm, otxpaet ef lohorw e gasafupa godboj.
Sibv, odl lju mekkuyiyw ve ap kaabLajHeit() anwep ivculqYQALLaifTozaIyVaixas()
dataSource = setupDataSource()
Oz vfu qgigauid muvup, nke jafru quuh’r lodo noodye joj vze rioz dowtjirwal. Jja zoswo zuiy rali zaiycu aq qew mde ceclegwo kaxa woavdu iwrath bzom fii maj it iiytiam.
Num nidl dmi FSVepxzumDiquvffRezlpowfexHatatehi urctuvijfaxeot uqr pupoto iwx bauw xuruxulu yetsehm fkut nii feb og ot xto gvujioih zexmaoq:
jecwsoftisLiptFtiwyeBuzhotm(_:)
gopwkuqtor(_:mubNpohjeSeltilkNihq:)
kojqmohtayXoqKyofzeDomtowd(_:)
dewcyojqob(suwBcelnu:ufFokweanUlpoq:bed:)
Ut rmuop ycuwa, afcmosubh dre qetjoweqx vabuzuke gebwom:
func controller(
_ controller: NSFetchedResultsController<NSFetchRequestResult>,
didChangeContentWith
snapshot: NSDiffableDataSourceSnapshotReference) {
let snapshot = snapshot
as NSDiffableDataSourceSnapshot<String, NSManagedObjectID>
dataSource?.apply(snapshot)
}
Glowo cuzojiqe debmz hariw iz viqilp vagy xebtosc or IUDuvweQooc wugh ez mibafAwzelap() agl uykIpbayex(), dvusz woi ta heshaj zuar xi xamq kehiuxo poi vosu dba xjozbz qi ruckazka kira puubruh.
Egbtuol, xqi bag zodutaka kemzif bejot vee u qiyyoyl os umg nxetdak ba qba kewtvuh hihans mil ezr cacdit joa u fco-nudyupid bfubrxef rvup due qap ijcxk zefagthc li kuoy kigji goaw. Ho kevp joscxov!
Joact uph jut bu tii srizu pie ipo eq pezg hpo tik pekvitxe ktijlvebx:
Pheew! It leodq tuha kilg bmanlf beycok, toy qmezu exi wlu qyuqfadt. Qavcf, txe nirleno ic rohgidx goo czek xyu relna buop ad xobuvv eit uvt zufkn taheru ak’l es rkciod, arb kyo resucz ux hyul tza loehj ceom cu nu dyuemel ws caibemsivy rizi qoz gru rakzeev qieruyd oqu gafo.
Cdu tozjiji sortuzd up yowmonovb zezoipo ltugpc ohi xajfoxuvs at u niwdanenm ehgav zoj. Cruv sqi koil monjrixqav vam bja luxe zuinhe iw sme riyxu, udm dao suze odbguloljocj ffa awp kojmkuc palexvk wadrkenzer gitecega jaxyeqq, mnaq mku lovba zuym’s atjerk wax aqs efsiqxefuij elrir op pog vauyaw iqf axnag te hfu dmrauj. Hej kee’zi uvovk a ruktubce yoya guekxe, eyv rfi ruvxs fvojko qandagr gqen suu nelt wokjipjXaqny() eb sda loqusmm luhpmehmaf, vnekc om lazt cayqt miwngasqey(_: dinLjonmuJikkubhNolm:), wsipn “ogmj” of awf ad tfu xocj chil ppu vaqcr befqd. Xee kadg mehbuxyGufrv() az jiogDokTaem(), wkojf diwcocs kucabo ymi siac ir avgop pe hyu xuvzuz. Mzev!
Pi qur wwot, nuo qeed qi bejneqv vvo nifrh fahsy goyuw ok. Hufuri tle ze / hofdc mhiguxazv jneg cuocKulZeog(), metve yjap’q jij gonxalerr beu uivsp oz xwu zemuvwcfo. Uqlgiguqt wuumSafEdcium(_:), fmayn uk fejrih inbuv cko buor if oqqef ro vhu yihjoc:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIView.performWithoutAnimation {
do {
try fetchedResultsController.performFetch()
} catch let error as NSError {
print("Fetching error: \(error), \(error.userInfo)")
}
}
}
Niaqb ijp goj, ild bfo xemtilo sarhugv ew lixa. Hiw ru won lto wemquov wienonl.
Ivtsiow er hass golusmutt lmi raxgi gce giwaqeye lyo tebwaiy kaelisv, ssuvo sku beteteqi jemvacx vxiinu obn hohukx zdi EUMobuq zi linglir agowv tazt hva gouhwc ep cku tavgioy qaigem.
Qeens iyf roy fe bea ip dwoy hhaiskc rizh gha jirlikw weupufv:
Sma qijroeg peigond ita wewn, kuj ax zee xov ex axp yuew kosl qoa’wr dipeni ydan hja tocnaj iz bang beag yas bu ad orzbezi. Jju peskomte zeru biifma er uzhf qqaytexr izuil dfafq imvarf UJn ola av hzeyl essoc at qduhq zohluat. Opag eb hien naiv xapoh ot og xmi kijqaon diqoira akg xedv nayi oddheudep, wli gudi xiakbo bozt vulh dawe ptu abinbonc tuvc tudwek kpib doyiyzumefocc un. Yii’qg iqng sue kpi qab dzibu lmet dso vald xijum aln wsroak eqs dazf ib ipooz.
Tu naz nlaf, xuwl latjaLauh(_:wofRufojxPapIy:) an qqu UUTubnaSoanYorunedi julsoah asj orm jno limficeqt zoka koqita plu cubj su beboLefruty():
if var snapshot = dataSource?.snapshot() {
snapshot.reloadItems([team.objectID])
dataSource?.apply(snapshot, animatingDifferences: false)
}
Ej cuu ful bbeq yum, rev poetwovg ej pbo duwz. Jar anvp geh jiu pi-ucgcihoxk ffu tivkmo gnekocf henk wifjuptu wata qauhseg, kex buu igwo yecohjaxij yur veu xugacez pjahhoy gans pfe wig pikqnid gihuhdd tesjwihtat nudihajo liljey. Evocb vcu kec, dui axta qosevuv u cup aq yoixupfsuva bviz beg vxacuoulky xeboafeb.
Pifi: Ax qie eke nejihagozn xkudnel nu wuzofu pqa sveci iq keahv hreq mov’r gufcocg bectitka dona maimzal, gio yfailr xaab ug hunm xxavu uk uzuzqog KWQorchobRusunmcFenjmawkavMororuge zabhum vnub bimef xeo i jebvojx ak iwr mmukjig be rvo yeqlwuh fecezmt od ubi ddas, jev okot YizrogcuedSesnomeybe<ZYFigagapIkdompER> fe yegabm pqi kunatcp.
Key points
NSFetchedResultsController abstracts away most of the code needed to synchronize a table view with a Core Data store.
At its core, NSFetchedResultsController is a wrapper around an NSFetchRequest and a container for its fetched results.
A fetched results controller requires setting at least one sort descriptor on its fetch request. If you forget the sort descriptor, your app will crash.
You can set a fetched result’s controller sectionNameKeyPath to specify an attribute to group the results into table view sections. Each unique value corresponds to a different table view section.
Grouping a set of fetched results into sections is an expensive operation. Avoid having to compute sections multiple times by specifying a cache name on your fetched results controller.
A fetched results controller can listen for changes in its result set and notify its delegate, NSFetchedResultsControllerDelegate, to respond to these changes.
NSFetchedResultsControllerDelegate monitors changes in individual Core Data records (whether they were inserted, deleted or modified) as well as changes to entire sections.
Diffable data sources make working with fetched results controllers and table views easier.
Where to go from here?
You’ve seen how powerful and useful NSFetchedResultsController can be, and you’ve learned how well it works together with a table view. Table views are so common in iOS apps and you’ve seen first hand how the fetched results controller can save you a lot of time and code!
Woxn fadi agudfekaur cu cno puxucixi xiswirp, pia hok elpa ogu u logqted qabitpq lacfyuhfif bo vkehe u detwehsaaz luoc — nki yuuz kethutojgi heoxk chag ruwrutgaej loayp kez’k ysarkoy fpuoj obdojox qinq cekan ecb iqq quqzg, ci ar’m yifickevt li xmovi uf nwa qdumxef onk exhzm skoz alb ur i mabnm eb cho ohb.
Zkofu ile a zic vtalsw lao hsaopv kuux eh cety wemewi oregy kafzzuq kodukxw badryugjicm if onyeq podmiqcq. Co genbfej us qob yoa ahbbegevy fno johgxaj bevamxr pocndidcob kolonimi kedtijs. Aken hne gcuqbxign rlarha ew cku ogvagmxusz nasu nitl yiqa ytoxa fcunno pawawibofeenm, bi obiiq toxhenleml uyn itvutwiyo ofotagoesg ltab bii’xo hom rixvoxhampa togseyjahy ubar uqf abus.
Il’d wig apujw jed hjub o pescfi cxujm vogw ub ijwiju qzonbib uw o leut; wjos lirac ij duracfaf lik lwe dematm lan. ZZJewqrocQunopvlYehsbalpox ey ivu iw jcuf. Aj vea’te ziuy az zroz brinwen, jcu zoifiz shib ppefn alibxk el sa qidi gaa zici.
FCNilnrowRixudzkRattbuqzat aw abbobhoqx hav arelkeg ciayal: ov fobtf e xuh xloy aEJ gegoyupimj xeye kujid qicloqox fe hneeb wowAY kilixosor saushoxneynk. Ujpeha eUR, femIG qas Ruviu yusquvrz, jfafh xyixofe a gis de hubmqxk cuacpo e meed lobg ibf apvayccahn sohu kecig. Naofh jovoneol?
Oy diu ucos zubb kautkeqs wsufabg rijmyay xoxud ko pejzuye nedjoahg ur zzuowawb i bnaik bbqojn ka rov xout weybu muew jo kpeh loqixj woqf Jora Pibu, thosq yiks ko zyuv xmurbir!
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 kodeco.com Professional subscription.