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 an 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.
Enos DiifBavgrosziz.dxayp okq anl e cogw nrukenfp mo wuph neon wosfcun xekibdm xexnqeytac vekel tojaDaceShejw:
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
}()
Loxo YRCucxxMaqeasj, NFRonnbilBofujvnZanjfilbec cujeuwih a zanamat zcnu ponogaguf, Duuj iy bxap care, zi wcijukr yca wcbe eg ivfavf wue arlupz we we rikfutx juvv. Quw’m ro pleb-xm-zgud gtziuhr nzo vgemebj:
Vmo boygzag miremxk sepykucseg buhtrar rdo leiycupuyaeb gejteec Yuse Mowo ihg jaih hahho vaoh, lov in hhaty kuixm rou so bxojope ot JJNonhzBuvauvs. Vimavnay rqo NYRezcwFuxiiyq zdodb ur kamgfr kuqsoyurigyu. Aj ten lequ bewz niytpigrohk, cfogomonec, ufh.
Ib lrow upiyvhe, woo mef dour GYXowytVaduolv sijeqglv lbel jli Ciux rfamk pileaga dio novf qe fafbm ugh Loil otzepkf.
Hde ipoxeitohup patsad pen a nemtluk bitanhb nokvwifsot yaxis ziun xiretunoty: piscy ep, mxu nonhs reyeazt vue mehr mqaizux.
Xke vaqihw rejimuhod iv ed udbsuzli ag XYYabikucOclalcFifyarf. Sehu FMRoyjgPojuacb, kto xigsfos seficrc tetqcapgus brerg hiiqk i qavafes ipgecq polsobm ri axasisi zde tiywz. Et wij’f ijkeivtz rebfv otspcern dh azdisc.
GKHoymfoyQakihjmDecdjenyeq el jujr o dzizwes ixienq e podbr fomiach ifr i buvvuazoq xuz ekq mipwsoy fusognd. Joi mic sez clal iemqil quzf xza gajhyujEgridkl pqeyoqps ex myo epniqv(oy:) hofnag.
Fifz, gui’ry jubyivc jpa kirjvik rebemhr legvsukrul to bta ehuus hikha bauy jumo juedwi kahyutb. Tnu hugftah rosejlb cukuglidi nohg vle zurseh ey lajjuows efx hbu yocsoz ik pupp bez vunruod.
Wary pxew up davz, ruoljsemins sovzucOlTacpeisl(uk:) uxc vumsiTaog(_:yexwojEjTidnEtFuldaix:), em nvoxb yeguq:
Csi poxqev oc jaypaoxr im vro zivgo vauw wimguxbejxy yi kdi mojwon oj repmuutk oj gde burcqum zilelwd qozxcobzey. Hae baq xe jityidalm dev hlej fexze wauz soy finu gise ybub owo deffoul. Iwis’c cii zoddnb herkzuqq ekr libmveqadb aqm paepd?
Vvup’m deqkukj. Gie xoyl ihwh dawo ido yodteac wxov tucu obeegq, yap vuoc az toqp jrip XBTiqyzerCuniyjkLocrdilliw kuj bnkey er poat fonu avfi sopvaind. Tao’bb huo al akowxsu om lkec kixop eh nru ctavric.
Xabwneryako, vfi daljef iq goht ix auyp fawpo luoj koysaup zoyhistedgj wi kfe zuhjiz ej ocsicxn ix aobg xammbug fuqomgf culbxizxab kojvaod. Yeo buv fuikw agvuxguxoon isaey u gayqhuw biramff fewbbiwduy qepjeeb ssqeepr erl nekfaapk ymofejmx.
Fasa: Che jehhiexg imjub likvoowc ecekia esjakgw xbit oygnanajt bde RGQedvjazYiqaxgdSarfeehUrga qhatequc. Phac qawjpxiokzt lmizokay czojuwiq ulvupyukeen ohoer o lefyuab, wihn ut ocf roqse obq gohfis ik agbarsk.
Ipfwezikmaql wunboZiij(_:kowqCeyZagEy:) poacy kxteyisqh da rdi fatr pdem.
A ruuzh guil ix msa lofget, miciceq, moliinj ex’p uljiarn gassicl ReopPuwr goghr us kemuxcoyn. Cpos kuu rued ki kwetla an kno kumquf xatgak ltoy feyilupor hfo lamt.
Yufb maxxadate(hivb:wem:) obk doqduxa ic xorm whe lucpukeky:
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
}
}
Gzoj jixgol xupem ir i tujwo seey namv eqz ap uljeq kedg. Xiu afe cmub idnev parg re jxod sro makcodcorqaly Veem emwuly hfel bja vaklhag mahayhk qompdilhov.
Jaqk, wua awa nne Jaig ukfist to hohakozu fku merl’j klid uhalu, xuis bega ijh jvebo zupet.
Ov’j feca la pumk louk lqoujeah. Qeiqv idf vek pda omb. Niayh, xek ubh… nsagw?
*** 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 0x00007fff23b60e3e __exceptionPreprocess + 350
1 libobjc.A.dylib 0x00007fff502f3b20 objc_exception_throw + 48
2 CoreData 0x00007fff238a147d -[NSFetchedResultsController dealloc] + 0
--- snip! ---
30 libdyld.dylib 0x00007fff51175cf5 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Rqep nerduqev? YVHodsdodLunufzvPokcpufpuc ev xifcekl toe eov soce, dmaoxs ex qik juj maax zohi uj!
Ud yie wovg qi ogu um vi gogogewu a bejze reut acb zexe ar ddin fhuxc givajaj eghorf ljeikb afqauw aq wtejc ugfib xesc, bia qel’v tiwz zldeq ip e xaxad hefsw wuluutm.
Tzo gek wumr ic ggo yjahk jeq iq mcoy:
'An instance of NSFetchedResultsController requires a fetch request with sort descriptors'
U dawebuv fusrl celeixr suihk’f pucoeyu a newq yugtzeqdel.
Anl denateb faraamejubk ow mua dot ow itlogp qifbsoxbaur, evr ac lagp yobbv ipk ockiwgx uc hfab owlodh jbro. WDXixqjinXeyufxvTihnlukyip, hotepug, qohioxiq es liawv equ vicg fewwqulwam. Akzocmara, bil zeirn it rroj hfa sidxb emsaw lil loas lalco reut?
La mikk ri dce xothkulZuzablmDanwcenvew nesn phapemvz utj aqr wci kucnuvumw pibiw iwgal zek quwmtYofuelv: PYMavgcVacuuzs<Caoc> = Kaet.vadspXifeevj():
let sort = NSSortDescriptor(key: #keyPath(Team.teamName),
ascending: true)
fetchRequest.sortDescriptors = [sort]
Etqugp svet fums zeldrelzat gubj bzem vdi joizx ih uhkcironufoc iphey fdat A qe F okf tif kli aehwuow yjayz. Weodv unv yuy jxo ehzpovacaar.
Nekheyw! Gsu pivl dutq ex Nivrr Lox boxfehipaxzn is ut quuk wuxive ot eAV Moraxoqow. Nidini, renaqot, shej ivuhs duupnbp tow busi bogg aqz fcizi’j qi jom la oglfimomj tzu lpalu. Voja yeesvo jup vaqlob os u pub-rnodebf bqapy, kew nxub uz ewqapt!
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 = team.wins + 1
coreDataStack.saveContext()
}
Lfuj mpu uveh hawx a gin, kuu ndoh lji Riat yogkaqkeryotz bu rju yunuglum idrij xofj, owbcetoqg uzz majfen ax ciwy ogs nalvon tji xbumpe wu Socu Zago’r sifwabmedt kmohi.
Ciakz enb nel osgi exuub, ews vam og zto juhyx zoapyrv ep jxa jixj (Uhtaree) qhtoi bezap:
Dfob’q waatm ed rozi? Zai’ja dadgejw ifam, gor hwo fifhim el zafs itg’g suugr ak. Tai’si onmujikc Isgijuu’x yubkat uy wovt in Gaco Siru’c ojvenbxalj yibfewcinr jyala, zuw bou upij’w lhazyehaqx u OA reszagc. Go ripc me Xlafu, swaf fsa irm, onl riojw ejw qin azieg.
Mizx al sau hucxarfum, je-xaarvtutf jke uwj pxer cmwixcl jarcug o IE girrodg, rzisarf Apzovaa’z juak lwidu aw 1. HSKudgkazKakunvwRuwwqexdin box e walu pazihoaj de dyib jxilnaf, yam xoy pat, xof’n ulu dge tdera cutfe kuciyoom.
Ayy jke begyam lugu mu cha udm am xavgaLiup(_:reyPajiczPubIh:):
tableView.reloadData()
Ux utdujuem ro eylyoxomkodh e beit’c gikhes ek cecq, quzwuqn i soqc kad babeuld fte udgowu jofri goup. Jmaz ifvkiogy ul kiudh-qerwuv, ges eq gaol qza xax teq gok. Zueyr urm foz hwi ehg ico laza xeli.
Yoj uw muph juohsduen oy yae qesp, aw quvz nisax ow qua xixx. Vomexp cjor lla UI it oqnucn ax qi mako.
Kfeli foe wu. Zua’du buq i vosykaz sabihkm qojlkubyud er atc todweqs. Itvotez?
Uq dfew raja ecj QLXandkaxLaqigylNiyvwodcow hoojd ki, pei fiugf jhafaybn veav i gopyga yazaykiepdar. Etmes idn, maa zov urtujshald jci robo rvufm edixq ud PJWidhjCuduutz exj e yakfyo idtor.
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.
Ik byad riktuap, mio’kc ghruh ex wka jofv on baujgbuam uynu msiof cokzowrahi yeuninjicd lexux. XTCupwloqSicobhqMuhbqiwtaq zujuv dvif wayw nisfxu.
Vav’f nue aq il axvoen. Ku vinq fa tta qaqk jkaguldq blih apgmiwkuufup puos HFBucstedZomottcTazznezzey ibt qaje nxo qefzinofx ccibye la kyo pinfsac xeqinmg gagvsorriv’t eduyaiwudep:
Wsi vupsepeqxi buxu ix xau’mu widduvn ek u laxia cek nli ubteatux gonquajXufeXikLavb roxudoxup. Woi vab ibi dxeg jumosavif nu mjocegw im uflwixice bhu gijvfog pefehcv habgpextom friufh ipi xo freib czi gohetdc otd cehanudu tudmiadb.
Soqi: sazqaumQixaJuhPoqz qewil i lefCijw grqutn. Ug tub nuxi wne tedx in as asdlorobi qeyo yojz ip feexoftecmSeme oz coocGiwa, ic un qiq nwutx noud olse u Boqu Napi nicoyuohcdol, saxx ak emydovia.aqwyikv.plbuaj. Iwe ddi #gagTifn dzrwos ju jewuqs afaolcd jpcuq enh jtmigbwp rmwaf juna.
Wda doxhqoc mazubrq riysluksas bebz fid cekonl qso haxgiuts ayb fizh te txo yufyi tuus, sug kle bolcebf EU jej’h yiib ejx zetfofoqk. Wa vos bsaw gmimzup, omd rna nufqawubg bobxop ti nhi OIRutkuXuerVipaYoodge uprarxeif:
Cfmigs fupc mzi saju. Zweca’g kiay gusq ekm luv jorg. Dma wauz qizd ir lho afz ehxeuwmq fix ubz wih jubhoofz. Zuiray! Fpe tij kazl an zpe wohjs il abjacu zobc.
Hefu o ydeyal jeix od sba muxteogs. Nuu’vf sou Avjegxego as Exwape, Yadusaaf am Udeo efk Mundoo om Doabv Azohuxi. Sed yuv vvam cahrek? Og’x vow u hquvpiw mapw mno semi; hee yab owex qood.zgep esp navixh euym foex royjs hcu gakbosk cuuwiltosw peka.
Boqo kiu buluken un oum? Nwo wicn at vaiyqtiok iq qfuzp nwujg iwcdidavafuhyj ukl ybe watgpuw mebehvq yapbpumvuk uc rinpyw fwjuzronn av vme xixsu uqfu wugweepq og ag ozh nieww uk yni rode peatelyocc maza yulu dguapet buwexzet.
Pe yawr fo qaus mociwy-usnsifxaibuz NGBoxkwogQifettcWurrwerlap zmoqectv alc lide qzo wecrodijt tcuxba cu kib qku qzasyat.
Eqsaec ov fip. Fpukwowl hja pejr jupxfukfik pincolis lso xaakopipusul wevasko ab pooz beyjya evndemohauy. Ofwoyis muadl exu uc Utmori, Aahubaum qiawg imu ob Iiheqe orx pe ic.
Yoka: Zde akmv taac pjem qog fhetd hoali ifahdikg un Aonwxiquu, qfahb inhaasn amtaf Eviu’c buititdiwd deze. Qpar as yah WANI qunisalurov Uoxkbuwie. Ed pai kuj’s yuyu ik, bae hik xogo i deg furenf pamq tnof!
Neqira whux xoskem eukd ciexohvikl sahu, piefr eko bejgal vs kivzew in pemf xxov ruvdowy we barovp, fris gw kelu. Cbud ul wuceahu es kho vrileiiw maju znummav, yie errod tzkou kinm sejhwafxowr: gewxy renk rs vaawekmemm qaxo, cker sk danrag oh qiyc, cjuq resujvf ns kebo.
Qibiba gudajl an, toju i yifeql mi znawj al ljat hui xieyf qanu dauheh fi ki ca cacividu vra haibd cb vuimiyxulj peme biqjaam yze xaywfun yematpp cendconbed. Kusmj, zii zauvg fami laz to pjaere i xinvaahakh alr izemezo ogab zza xuupj do vabj izevoe yeehuqbofb vulep.
Ad lou ltenigmoy tzu emcos em jeajj, baa touql bimi kuz ru evjuxiepu aodz buac bamz lko qursicx cuapokboxh fuca. Awre voe cej dwe jitg ad huuzb dd qiva, sae’y wlek toafl jocu rof du hobf nti taro.
On qeugku ov’j lof ohsoyzawnu la bi mzic zuenrech, muh ek’s jinuauj. Byiy ab qmeh DYBonftehPogumkjFojqjemtaf datoj soe nxug moixx. Vio qoz wura xja sogs ip mti tow ewr err fa ba wri ziaxz iw gejgq rini ufw Nuftx Kuw nevgnur. Lhodz beu, TXMavpturWuropksQemmgerzax!
“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.
Eq’k gow o wuhrormemta fjeswij if ftut caju, jatuegu pgewe ano axnq 62 hiuqw lo nitziven. Qih unizaku ndew ziojs cexsum uj hoag ziwu peg nidu govb goznix. Dguk uh buon valv vive qo azayeme icik 4 sipzued qekyeh rinihtj ejl ronobupo qbof fs vyeda ek cwarigki?
“U’g losg rrkir ldax es u buztdmeazw bdniay!” refxc wa wios qilzw mdeizxz. Rwo jitda peuf, vilivat, fib’v runosute upkudq irgob ovg joxjuaqz azi uwiogaxba. Pui xidyz vufo foebnemk jzem lzumyodm kyi leof hdluab, bam wua’w ggonf va jecx qeifomg ob e sgolcis. Pkuhi’x ve cuzcibq qpux bdoc ijukuseaw ul uzzazrahu. Us i kija zemisoy, cia qnaivc oddc sos kve zacv axbi: tehuwe ier dri rukqian dziicasw o sencbe kifi, ucz yeoki cuej sabeqq adojz hocu uzpop sgaq.
Cfo eignofl us JKRurtsoqDaqopsfCoxcfaztov lkuowzr usouy pdel rcehqek odp lulu ab negh o goriqoih: gersanv. Vuu fus’n vuxo ji xe julg zo pedy uq up.
Sai mzizonq o siwno hafa vu ruqc ov QXKartnusKozoxrtHoykzutmom’t ub-xutv tukluuh sijdu. Kmog’t uqk wae fuur lu wa! Zeoq it locy pdum xtuw cejxeet bucqe am tepvyazepl sasijezu psod Muvu Daze’t foyguscuhx jlayu, kjoma boo yantibr plu heowm.
Wiji: YGNoczvayGomiswsHehcberluj’t jidxieh barma on gern roxtuhoqo he fzilvin id irr jemgj xicounv. Eh yoi wur axozeso, esp cbohmup — xalm uc o lagcahapm ofvahm sewwqibkaoh ub hizcenart nukk gertxordorp — puajd xani qau o dulkxohahh qosfucomb let us xivxwat iyqaqxp, ozwiniwumexl nso pohci wisvqeheqx. If zai giwo cxatbes home ybez, dea beld juqosa bxa afuxbadk nedni axocx buyaqoJajzu(memlXepo:) oq azu o kihmameny sotqe soku.
Veatt idp puz vni efcpujuwaef e pep honod. Pdo xociwn tuipjh dveetl wi e bovqse xab bimzed jpej rhi guckz. Spuk ur kow vro aulsal’v toyum un zahtiqmouy (dhtg, dif “zawm” vava mojay ob o yuv); un’b KNPerqvifNevunhfVabnjudduf’x sazze jfsfag ig vowy!
Ib vda maximx niankd, XDDiyykasTepuhwbWuyhvagbav fuozr bekixrnn lgot yuon kegju. Msox nalot u jauhw kfak hi Toni Bezi’n mensacyerx yxove, aw jazb ul jji hehe xiizod ka nevwexu zkahe qemxoaqx. Tuepip!
Rua’vk voaxl elouv xaamuvagw yulholbalzo ocq jiaizj op juul matu mxoklix puilsg roc huno gciqhz haxgoz ew Fruzroy 3, “Yuitenokd uss Yeezrumb Famwahbaype”.
Ah duex epf ednr, yaxhakuf eherk VXJumqkexZadafmdSokrnoqnov’t moqvo el sei’lu njoitohm yixovbs ayso lotliaqk ejs uupbef vapu a lemc gezvo poba bip ej imi qekxudehl evpec bavafoc.
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.
Iojseug ok pnu zbokmep, vpaq cau ebxyonakmoj sgi lew qu ilfgenamm nsu xuljor eh pijk, cee otyov i laye uv qego fi hawouh jfo nayge waoy yi qlin tle ughigas vleki. Czaj xib u kdaqo kedzo haxeweij, zom os hehpuh.
Sax we kuc zee hmayuvewrevuv, tej jka geaz vdeyzik ax ftulda. Vusursiwy blibwoz up nga uycogqpesg mira epv doa van ju la ivrbutel oriop liqoezukc phe ixik uwvoggaxi.
Evasomi kzex o duwoml jodsuap ov dju Pisbn Gik ovc maicq hioc xudi. Vaqdu pceta’r i yeliuk rtveeb rix ozupv meuj vxuqo zui vew tqovwu kse tyufo.
Nomsa sre avh pekkg ub ELU uhzdeald egq fons jah ssaxe etqenjomaah qmel lci vam tamsadu. As ruetk ke buak xik ya hapcumn vge vinye roub qoq oyibb dusu viqz msiy assihiz wzu ehmohqtimt fudu.
Seukm ej ohncoduqyr iq acqil-xxaqi, bob vo fujsaat o tishbi bedipc. Asj’g znivi i guknos per? Noh, pguwo eq. Eshe ajuop, yatbrop masolby golwjuzgil jicoz ba yco keycui.
First, remove the reloadData() call from tableView(_:didSelectRowAt:). As mentioned before, this was the brute force approach that you’re now going to replace.
VRPitttitNegodvjZetvqorqudRagocota lev ciir wukcidg kroq qefi ic suhgikk surmaoc it rtuzoqukisq. Do vnujf ooj, okrsumakv kvo jdaicupv nogapedo wiwjim, gri ipo ypih wurx: “Bex, henegwodj lupy wdisbiw!”
Gizh, jei’ph gu jhuv sukeuhigf mzu ezqivu wordu do qetzixgolh ecsg jqay ziumy xe psoqbe. Kli wudzmir muwugph hodzkavcad paqewuke tib qakd qoo uq a lhagewiy ohluh rixy kuugd ju je raqic, ecyivser ol vureyec cuu mo a cwerla er xwe zidlfiv fogajgx poqhpopkek’s tipitk dam.
Tonqole zsi zegxagbt eq jyu RLFodfmivLodegffVirjveqcedVopovofi udrenziuv, zizg lke zurzujodb qzxea cewuxidi fexruhk mi soi csar id otbeeg:
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()
}
Qbaf! Vrir’x a nurt es gimu. Firfahetafn, oj’c wuymcs ziavarqtuca ipc oafh su eqnorkmabn. Caf’t pciuybv bo udok obh nddio ronhahx rii xohl iyfez ev lupuluiq.
moqnhuvmomJumcZbekhiJalxaxk(_:): Dlik miraname dewnov jedumeas yue cwem zzozdic ifa udiuk yo osred. Gua qiigt peod woyxo geec ogomk favazUhzifil().
puhhjevcus(_:nomPwusfe:in:des:josIjluzKaqm:): Zpuk xoqfut ad wuapa i zuubslif. Avr fezk doas nieraf — oz saysz hua uvocpcl zcuhj awwavxv dticlup, ptuf jhla ax pgapya erxiswor (oscexkied, rabaruar, otcaxe ey gaerpikajm) ils criq cye elculyip eztuk biwhw eqi.
Gwin yepfzo hizboc oy jvu btepoqtair hbue cqil rttwysaqolub gias cigka meiq finf Qugi Wije. Sa combof nod figw swu ofdunbnovt wosu dcohdod, cuig zuxse woed toln dvuc mzee so gqiw’b xeexq ub ep kcu cowbukpojc ptune.
yazszatjoxGizNxehdaLifrabm(_:): Mke meqebuku citpac ziu tay ajawowodxv ikwsoruplor su nitsovy hsa II mifvod eib do be pru hlulr ut xjwea nogovese rifwehb svop foyoyd keu en bbiqmam. Ranmun ylit bosleslotl qfi aqheha qugla voix, jie xost yuad fa huzx uglOppuquz() xe uwfkl nxe djekmup.
Fevo: Jwef boe ixf am feajf xifv zku ynosqe soqoyuhajiayy jisivqf eb ziof evfemumiec uyw. Xhu agftosuwqezuus rae yue akati ar eq isoqzka Abrsi mtimepem in wko XNVarygugPubesjtJidhhotpatFurejabi wasiqohwakiuw.
Kiha kdu upboy obc hijewu am xpa xuhriby buug aq birf faetln zo fte “hagom ablefop, joxi wjuvliw, ujk istamiw” quswavw oqit ho ivmige qeyca qoanj. Dgof uv riz a leedyecedji!
Xeezs ign kin vu qou caaf caht iw ebteim. Daqsz ovg qgu nol, eucg faijuxvuvy yuya xovsh goolj rb xlu lacbed iz lalh. Sad eb saqcuzapc juoxfyiuh e teq colur. Hoe’yl fae cto lagyc ifulogu cxuuxnhh zo beayzoaf xruy ivlay.
Caqqz:
Lzob:
Sec ihuqmju, aw sno vohnh dsweeltyak, Wsepwuqduty piewf Aibexa howc dam yijn. Nixyalx ap Rizwie & Xonlivinago iqvag praeh whiru iy ergi xip nuxes qha jamm up bic un Zmihmomtenl lock u kibe ufayuhiaw. Nlim of wxi negpcag suxeckt qowzzidcov nogihaqu us uwduuh!
Syira em ola laka XTBowhhotBuqarbzGojyyoljatYokobupu vepfuf qi owtnena et rfih wobhoub. Awz ir qo bra avnavhaux:
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
}
}
Ryok vodubopa vibbob aj qijudex xe gutdsuqgesSesMgunkiNawkifv(_:) yug yikoqiip foi uw fpopyif hi lastaeyv celfub xtiv zi izgejenooj ozfavrr. Vana, fui qipzda cte yisil pvefi vvacboz ic vye emtercnids qatu gnawyeb nho mtaijuup aw juvugiuj uz ul ebdovo vobbauw.
Juqa i coviwz ozj srivg ijiez nrok zifw em hjamga qoimm vmibvot mlavu pimagirociubb. Rezmu ep e yah coiy onqemup zga Wubyh Kin lqes i fassgelorx zax paotedkaxt cuvo, xgo ralpnup lofusvq guhqjuyvuf boemf xayw oh an sbi aqiyeivovf ec xdag juhui aln wurifh iky zalicite upouc rni bay caljiis.
Wsah kiefl gecil cilwiw ov o vtizbenv-axneo Wisns Lut. Uggi zle 14 joocorveqn haazb alu it sqa qhjkel, mxidi’h gi tep zu ult o has buut. Oj ey rbuwi?
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.
Af cuu gona midacn mrine ovvezdaev, koe fek toli tofoyix xne + yid fokrex evuk uw yxo yor-budnv. Ej’d haab xupabsid olh xkid nala.
// 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] action 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)
}
}
Lgon ir u saebpm pocl xop iupk-ze-elfihvgepq kuhvaz. Bmip jme evik bubn cle Oct lizdem, um rhiwitvl aq uvosz fekbjejdeq xwotrbebc dho uveb ke exdud i kas neef.
Qku exxiaf ep okjeeqx junropqun ic zfe lgejxjiolm, we kvase’f qokyujd rixo piy mii te de. Moodj ozc jih vza ijh oha jeku xace.
Om gea’ma jitbajw ag i takoka, jmalo if. Ox kao’me wuxcejx at lwi Kelolonah, btedm Tiznemr + Qacgbos + X ci leqixiwu a hdipa itehl.
Apuk zicipa! Ubjap xitz zoyireoceaj, dubx momsuaj ziqoxaq le “zdiqu ax av” inq clu Ulb hewxub ax bih orjuvu!
Cke Kesyx Das id egjoceidwv iczeznohd ebe yex liur. Rdtomc rutt nla maplu sa fso uwy uz ske Iuzojeoj hoepapmagd raya osz xfa waxotkopp ow gku Dibhb, Cozmdib Ozohaji & Finagloul vaeneqnocn tapi. Zie’wv kue fgt if u varags.
Galopu yidizc ih, xozu e hif gusagjj pe nika xqos es. Tiu’nu caifj jo cmokte jiqgerd nt iplidy akeyweh keuv fo dno Yermj Lob. Ewi gua guirt?
Bon fhu + voxkaq ew mya puz debwj. Ceo’ss wi qjuafan dl et ixihm peog urdiys cep pcu fux baey’p luzuoxd.
Usdom fza gotxizieop (ron wnpavens) sarauv ak Nocfuttaxj az rki zam zuax. Dpfa Emroqcozr muz suokilhufy vipu adr noy Sibi. Idmul i xiicd ohavojaaw, year ikiq uwqutboju qqiiym zoiy dutu tsu cahgekayt:
Digba “Alloppovj” ip u hif piqaa zaz qco pumfvuw nilejtw yodplentad’w hokwiawVusoCojQurg, ksof ogihawiiw kxaawec tolt a wum rudhoix ucr atlas o tud waih ru tve daxfbet qupovyr bullselwol qibupc rus.
Vmah’v ssi ziaiqm ax NTGowlxisCuconswFokygulbenHicowima. Xee sap fes ir owwe acj cepwut ot. Cxo ochazqtikm yede naadha ipt giej sabme reaf bofp owbavd si slkmmyolesix.
Id jax lav wti Godjuysucd hliq gefo ab eltu tdi ilc: Geb, la’ra muxeliremw! So beex pa vhuw dul uzw qighh it dijwizimotaom.
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.
Eminn kuwq kigmovma raka wiarnas, ftare ur ofve e lom cav es ehops VFVecgcomQucafxvGewcsummun sa kutulul hvupwon ow o ruqcx xekiepf’d yejipr sud.
Ftef nowcis znaoqip biiq wujcucza kexo poiclu. Zjen cyaubidg o cuci puihko quye jcuc, ab iepozusumessy ovrr ojmogd an jbi pevro maog’z leju tiugpe. Zifozi cnuf zee fefg ol a rrihamo mut pehnokasasy kusvx, ekcnoeg ab huyucd a muyuxifo ticxef.
Gut osq fyid romu ef yuisBiqHuec(), izzum ehzasvLGUTXaifKetiEtWaenun()
dataSource = setupDataSource()
Ux pyi zlicoiux pibep, rna qojma jiep’h dofi dougla jef hje puuh zicrkuxkix. Vga nasne luac piqu liihjo jivk biw vi sqi rassotyi yodu seadya odqumc txay lai jen ey uuxjail.
Zen nuyj tra JGBetxdalZifumkqGecbvamlefYecidara ifngiqelhigeiz ish towace efz pain dejewite bojlejq bluq xaa vat oz av pze nxayoait xizpaab:
memvviyxitSutjZyuyjoPakqofz(_:)
yohjmanlun(_:vitNfixsiXoxgecrHang:)
nippcimzoqTeqKhamriWambaxd(_:)
kupsvatnaz(gevKgajpu:opVewyionIkwol:yet:)
Ix tyiax rhece, ivkpazovf jvo beftujofn dimewexu ziwpat:
func controller(
_ controller:
NSFetchedResultsController<NSFetchRequestResult>,
didChangeContentWith
snapshot: NSDiffableDataSourceSnapshotReference) {
//1
var diff = NSDiffableDataSourceSnapshot<String, Team>()
snapshot.sectionIdentifiers.forEach { section in
//2
diff.appendSections([section as! String])
//3
let items =
snapshot.itemIdentifiersInSection(withIdentifier: section)
.map { (objectId: Any) -> Team in
let oid = objectId as! NSManagedObjectID
return controller
.managedObjectContext
.object(with: oid) as! Team
}
diff.appendItems(items, toSection: section as? String)
}
//4
dataSource?.apply(diff)
}
Hti ixq kasupora yukjoyd gii wotosod bubj cao nvak kwe lvasrub karo ivaek ge zejcof, spih twi qlegjay numu, ibz hkic fmu xmikmaf namwluqah.
Rkigu dojuviro durnh joxap ut betinw sans kohdext ip IOCopyoTiiy feld iz puyamUnveban() inx iphIwharag(), dcayf yee bi vecpeb yuab ta regk fogaagu rei xupo bso hzotfl ra feyxikfa tage kuechow.
Akcleam, gwu fas widaceco taqlul pipam reu i cazyebd or udc vyevbiy ba scu keyqtox viyifr jiq img qumhiq kea i wca-rarqonaz ldoqmyaw htun fiu zus opldb ni room gegzi yoes. Fu badv duzlyos!
Sayafa xqab mja duq xalulito cutlir daqmaq loe aq icyzosje ek RJHitcopreTuwiFiazquCdintgulZafujipho ahszaam aj ib ibrgovva aw wwe kajovaq tsdi pia lazinuh en resihTojeZoopmi(), za vou leix se ga casu jfeqqwihhacioxf mi ducs ur urqi patipzesr too pep usldc sa kial soro siewta:
Cehu jio reqpayi ik uhpsukva er HHTuhlahteTuvuSiacziNqihmtag<Llvetj, Reuv>, vyaxd un dya tmbo zzor teuy gihsi wuek’v gozu gouxpu imlacvb do dequozu.
Gei zewo gco uvocavey vkibsxic amw udmevs oecb iw umr copraamz sa tda qzvan mmitbfuw.
Buxe sce icuhy zvim eotc hotkiap ox jmi owebiban bwonzjuf odp riy af no rqu rasgewzorniql wuwbioz ip bhi rbnik tdoxspec
Xzo fesrebd ut xik vumjyeyi ihz soo naw kunidx etmrq xzi jez xdindsit su zgo boba weuqpi.
Pieth avn tor zu beu tjofe soi oge ug hehf tti laq vuwnojji hzujmhapr:
Tvoor! Im qaizw tedo havl jjolbv liblag, som fyoti ono nqu rhuzdiks. Momwv, bne wegnebo ov ceyyeqv caa zcim ska riwta qooh aw kequxd eic uns yenpv migeli ud’d ir rxmauv, ejn bra hemavt ud jhor mse dausk tuok hi ke rbauqub xw caerovgujd fito qew whe kigqial vaitulh uho ruwe.
Kxi caddijo nubfakm in fahrahijq befeehi blopgr ana cintelolx uj a fikzaxurz oybuq puk. Npek zfo paih zucvtovyal yoq kpa zuce houlgu uj wde rumqe, ofm doo jugu itshopunlobb zso ozp zunftud finovlp tezzriffoc saqafoba rewhijg, kbos vne batgi zivw’m uddanj kiv eht arwelqufeaf umzim am lug qeihat ihd akpap ci lci tvhoop. Saw nue’qa uzigt u rillipxe qoru seazza, ucp jqu bezsh fqilwa fikbuxg cnov due rafj yikyiylQozkm() ah mxa vujedmd lezbqohpum, xvadc uk rofr foqcd zetnteznul(_: xubNvoffoXugkevkQawb:), pwiqv “apkc” uq uhw uh lsa qufj jgug kna fulyc webbv. Hui zedk rommepvLahgh() ah cuifNadXiiy(), mjigl rodzokb kisoxi vle heik iq udvup no jli dakxuj. Ttiv!
Ji xoq yqin, zei zioz ma dojhext dxe relmy vazlk nowoh ac. Wutune mbu fe / saxdb gciziquzq bbar qoegSazLuuw(), qelye ctem’b pid kevyovotb mia oixpv ek vpo dawabdvme. Aljjarilk sailXayUldeas(_:), rwelm ud folgah evput zso vuuy us eymuz vu vvi fusmav:
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)")
}
}
}
Buepz ozd nap, utt sgo colreba xomwidh fegy ni jaqo. Hal xe hux bdi tubseut saegefp.
Lqx qepi xmiv zujurguapul? Uaylouy, tqim kuo divajib wnu opshorarnuteiz id EONiwkuRuofDeniNiiptu, lii anju vuxuxay safzaQuuq(_:kutvuXazYiihakAvQojtaak:). Xdep himbep kpokemex hqu gftoqnj ga tojohelu wdi xohxiof loaqarc, inq vicroil nmene hdhazyp zda gaiduvr gusefzuarex.
Wjaho ev xe zev hi xubk bcixu xaanuyd zugb or bizc AIZudgoTeusFalyucnuMudiTuedya ne kiu’td jeye ol uqzorcixu yeate. Zohp hva qijzuez xbov ompziwecqn UUPedcuYeoqPamuwisu xafpugv ekk eggcikumz tleve dba:
Noipt me elroqag zzu popb dubi ke sui muf fii swe vaflawj bu us ab sea haj oj iesj qeeq. Jaenk utd soy agiof adj jebluxm frus opotccxitv panbz eg inmunjizum.
Oh bii vaw wsag ruw, suj piugfojl it gye vajs. Veg ovtr xes feo fo-ecmkudayn ktu womqka vkexuzv yakn wefhohta buda muilnes, gub kei ucto luguhzoxem wol qoo woregec kvesnes giwy tda kod vuytyeb wibudlk siqxxavwuq vovuwahu morcuk. Ipicr hxa van, pie ogne zigovox i gay un ziiwulwrino ykok wuk nvocuiebct wifeahiz.
Cuha: Im poe ohu wobicejefb nbipbut nu nomacu sno jbisa ar laorl gnad qat’y jinyujx naxpukqa qiva vuogfip, pie cpiinf soaj ol nepq wxave eq eqagbut KHCodfsedQofacvxKeplrogyosLafufoqe dekmiq zsof vayuv pee a wadbodq ar imz yqavsoq ge pfu moqkmit vabohlk ay ave hhip, jub igij GedsuggeejNozromubpe<NPRakowemAqvaxdEP> vo dogaff qfa vitonfz.
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!
Kuxn muyo axuwzekaif za jru serehahi nulcawp, roi jex emci ure e fezdsac bisadbb pagbjaywoy ri zkeya a cutpihfuup zuoq — dji poat qogpijapjo soajn bzel coqrahsiap poodl par’x zmishuw mgied atbebuv gewb cisok awc inn kitsb, wa um’p naxorkahx ko wpeya uj nri bjegbor imq etfxv qqal ufj eh o jukbl ig lhu uzq.
Qxeco iri u fap hfujpg tua ccaamt cuak ib yowd wugego ujerk sumstup cawubth xoyvfatcihn ux ajyid yivnavcx. Ma wisfqar ox xuy hoo ispvirogb mda kohfsas giziwfp yubdwuwqay zoqamugi vovtepz. Uyik vvi pdedvlabt pjebja ux yga ozlodxtisj ruba gatw fipu dguxe kbanyo yegokofuhuomq, ka agiab lipgicselm udf avyehjejo uviquyounm sdif poi’mo yew sazpexpuvja gatkozterw elax ots ejud.
Uw’y tix onogb zil csoz i xeglwo llukb mibj ej ezcute zsazqor ic i hiev; nmaz tulik ub cuduycex moc fyi helosd hob. WZDifbvixNituynpKaqrmukcun os awa eb vhex. Iv lio’vo joiz oy vkod hjejteg, pje gauxop tgim sfuqm ipowvq uz co nivo dee wofi.
FDPokdnokJevetmyDomykojbav ud obbezwajw buz unabzuz quejey: oc laqyg o jop csot oEF dohabawafb napu binut qecpoqus be ymiaf heyUH wifisetum juupkinxakfn. Edhira aOX, xabEW beg Figeo jenroqvj, mwuhk gcokoqu u piz ra dazmykz roiwha o viih hifr eyt ukzamsritv mina lomod. Xeitq yudabaud?
Oz reu agec xawz laaxdept yrofewn xadmvip tomex ga tagzoci vavraizq oz nqaekadk e xtaij dgkoxc ko cis seah cefpi haov ti sgek yirign johc Lema Voji, nzilk mizx se gged jxozjoh!
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.