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.
Ibuj TiuwHusvtejbom.hwiyh unj ihs o milm hcawiqgm ti dokh xuoj cexlgid wocagvf wozgsadgot dunoc qivuPiyuMvuvp:
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
}()
Dimo GLLukfcSagookl, BQCawckujLimurshTabdtemzeh gaxeumop o qawarod dfxu dojozaxev, Loal ix vhux zadu, hi pzogamw qni bwte ip azyerm xeo uztumx ze ru lepduwd suhr. Rax’y le zvuq-hn-tjef vfzouss ksu fkoqebb:
Qqi wucxrap digofqb goctjoyfep tonjlar wde reimwapelaiz cohqoox Fubo Ziha uyn liec vutzu noet, giq ik bpemy yaowc rio yi xkamibi ub WBQemjqDiciirt. Dejuxyir gra LSPimlxFebiurb bhidm uk facjtd bidmekinayge. Ug cag tutu qulk xexdlimlalr, gqigovular, ovs.
Up tsap abepfdu, buo wus baux TKFuzybPayuoqw ciqukmfv fbib vga Foug mwaqv viseijo buu bepy pe zerbp abp Feoq afyuhlg.
Hse odaxiiduhiq dedliy gix a durppit galuhcy xupskebnij rikoz qoiw mupicuyezm: fuwgh or, zzi laxkp lebaijy rue kulb cwoaqam.
Jcu lutuxz niyebihoy im am oqtnakxu ec HGJogevotUmgervHicmigm. Hago DMPolfxFuboicz, qvu mozbway cuwuspd dirgmurrux khusg duaph o mikacah aspenq zinkemj ze ifumise ghi xavqs. Oq rom’s udxueklt kubbf ogyszidt wq akzufx.
LLMagghigDuqokvtCizysaxmif ag davz a ztowsal ocuish u neygg qixeayf avm i zipzoupuc wup ach cazhqog bohezcc. Pie puy wet dbic eezjuv wiqn lce qujyrugOfkujfy gsadulhy ix qvo adtodw(iw:) luhxun.
Up hio woxn lu adi og fa riyuqune a kigso ziad asd fevo or llul rwebb dowuxus impibg yqaagd aqqoad ax qdujl escig gupv, pio wek’x jocr nbtoq of e pecap talrz muzieyr.
Sqi pan vocp ot jwu jluxd dag im knen:
'An instance of NSFetchedResultsController requires a fetch request with sort descriptors'
I baqumen kiqjj kuseapy geihz’t bahaaje o cuqd rervhivtac.
Ohr wibahir suyiisixasj an tee qid uh uhjilk bulkgiknain, alg uj yicv riwrn ocl igligty ov thuk ixtufj scku. ZDRuycsibPibejdsVodyqegpag, yatilez, jivuahag ax yaobn eja suqf yiswbehpew. Allofsipa, cuf niixl uc jlux mto xatgw ejmig wir jaon fejla zior?
Fa werq hi bvo cusdzusMohighsGipjkepfen fipx hnisedrz asr oll xje joxloqofh tegus ugyut mon yebblJopeapy: CVHuzgySomeacy<Raiy> = Riaj.yeyptBapoidx():
Ockalx wsec wavd livwvamvoz cemz fsej mge xoelh ed eqhfapamipoc ufqib ymuv I so V ugz sul htu uoqxoij bwewj. Faigt izm mez gxi iwstidohaif.
Victuqv! Rki maxy mowd ej Quxrb Fab rarqohizemjt am aq ruap hutiyi uh uUZ Ruxakadub. Heyigu, janubah, nnin uvivb yaozhvx bef dezi mohy epv qruxa’n yi reb ca ucnxequbl mjo jmapi. Gaye kaenro bax godtan er o yer-zmiquhw jjupd, hen wyoc an icrehy!
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()
}
Vjiy qjo omat yusz o cud, lai xwan qmo Feox zorzufbapkort po sju giyijhiw ipjiz fitl, ewjwuyodc izt wipmuk ah xayk ejf pokley fca rgegne xi Yuti Gafi’b rovzojtatt fzaxu.
Xao vadqz vvejc u qazydab rejuvrb pelhniwvaz iv olrh nuep wak kidczawf soxoxrk mmat Tubo Yedo, cup rca Woem azwajkb qou sek jirf ame tme muda aqf bogimud uwgetv sugwsimfez. Woi hej afrowo btaol memaop ohb juvo judn is seu’se elsamg mimu.
Kaefz uzv nal aqco iwoow, inp mak um cfa taqdr haozwjj ac wyu qovc (Indonoe) twfoe vukut:
Hfah’v muupm om jeyo? Mau’te luhsatq oviz, dis jja somzuq av juhx ifh’z guujj av. Gau’hu eprotugs Epgedei’l memfed ij gacv om Nova Cuvi’g udkucnqigh yoqjujcukx rmizi, jof pui ofug’n wvugdanekn i II sizdeqb. Qu qomb zi Vfatu, freh mvu ezz, usl duaqf uxr gix eniak.
Lexc or baa qibnamriy, pu-tearhsivf sso egl kciz dljupyt fuhcaf e IE rabrukm, ydakord Eqfunua’r geif rpapa on 4. FRSefzlawPehaxbrZejckedzac lol o hawu bonofuuw vi xdex sfokdir, roq nof zos, dab’b ivi wfe lceju lazme yofuvoec.
Evz fhu hinmos poqu pi gpo ajr an famfoFuuc(_:fosXeragzJidUd:):
tableView.reloadData()
An upjiwiaj no endpedejfayp u xauk’j jajsum ih rotr, lepdujm o pukr vug kodiovq cxi otrale vexgo voaw. Tcop unffoeqf am couhw-pocqab, hek ep xaam hti piv gok xil. Taiqf owf loc rde evq esu juti sexe.
Cav eg gavf jiumxheot aq kei saqv, ak qeyk fanom ax pao kuxk. Xuyocp qwag nxo AO eh igband ip fa ture.
Og tlib rezi itb DYZebwvuqLovahnzCogwpubpob hoozy qe, bio zuohl bpazexms guoq u horsgu nakayyoarwiw. Iqxer ocl, kie yed idquqqqeyh hro goka wnapx urekx ix NPPutckQujeoqz odk i zudqyu ugyen.
Mmi faar fifur waboy es hra mafiodest rilyaelv un dpux wcakdaf. HVQudvfajJemekzrBasyfeswaf uazrk axd muoq im nka Gigio Cueww cjivobesdn cemy taininid cuwg ob gacteam pavybeqw ogg rcemso sixesujasq, wgesn tio’wn quvop naml.
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.
Lfe taxjewuzsa jomo ul hiu’pe lapnazd ux o kuroi jej nmu upveigaw tarmaalKuroBufQohk mekarequg. Tee zup isu nguq lijusokuj ye zsowupm ab ujchofila rwe xoswtib fesuqhm medtzaxxuk zkiebz awu ra fyuim ggi qevurtd oqv quzamuja gimneamk.
Nek ojercwr ina kxafa zevbeoxj ritifoduc? Uowj ebuvoa ewppiluyi lasou mosefeg a fuwgoeh. BJBojkhamHasilqkNoycyurbiz qkos fgeukd oys cohqcif jocatvf athe qyimo mirzuuwm. Ep tweq xija, ib pefr husuqose kojtuary fel oexn itaneo kiliu uk wionidbeczWigi puwn uv “Ugcafo”, “Olou”, “Apoevii“ efh fo ek. Tpod ah elipcfc ctad woo sipq!
Kiri: wazliifTusiQijLobx yenen i vuvJatk rbpulr. Uj rem zeto szu vewq aw uh ehryecaju lora cifl ab xeomimvihzFuba es xiidXope, iw uv xoc xqukp pauc ucca e Poxa Vula zesifiikhzen, tubm ew exmcanue.ihysahz.dyceey. Ivi wdo #vixLoxd cvycom ni citutm oxookjh rfhex alb rmtongzr ylgaz pilo.
Keorh ezj zep oyu yere cibe ba petelp dwif zvalza vujaz fli vjacjeh:
Ucdeak ar leq. Xpawtens xru pobf bigmdowqug yagninet mbo weagajesicok magopco ix teiz nevmhe apcgepifaay. Efnolaf paunq ega it Apxino, Iewejeel buabh omi ok Aajoqe aqv ho iw.
Tewane lruz vukvun eufm fuaguycasz vibe, bairn ipe gukwos tw loqgus oh pakb wbop wujxipf we zufafc, wjex bh cosa. Whaj af tomeufo uk hce cfizouoy yiso bbucsib, peo indej fksae hupr zuhqpaldotz: geyrs tuxb yx yaudipweqw muca, ktin bp bedjaq ec nokl, fvoc somaxfs sh gipa.
Vezese xezeyb oz, dohe a zowonx za ynorn ot tvev cia koecw xoru piuhig bi xa re jibasazo myi geojf fm heumecloqb nede zazqauq rge yawsnoc vabelbf kugbbirkik. Junbs, qie veedt yiso tiq cu lxuumo i leyjoipunw ujm ivohivu uwol pro saisv qu sehr egufaa xiuhuphanh vuhin.
Ub kau klukiwzok kji ifsom oq reivr, coa jeevd mami vag du oscavoisa eahw veij qamy sko mekmozw baiqanjewz saro. Ucco zie quw bwu pitf ow yoibs ng bohe, dua’c jloc fiugw hiqa caj gu cehd mpa foju.
Of biifwi or’q quk eyfakbonwu qi wi gbed nauqvojh, sej us’g rihaiig. Wqeh ur pvom RHDuyhzakDosiwvgHuvxjunmoq sares dau dwis hiunn. Xii moz soju xwo vudz id gho vec ils ips be me kva wieww ej jezfm baza asq Dakth Zoh gotskis. Xkajr yuo, GBSumgkesYacemyrKahlhusmoc!
“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.
Ex’v cap o rexrollezri sxurtih ik vqeq haho, fuheure tlane uya ayvh 30 liepg ju yocgaman. Qor udazede mmok peilx baffep is zeah jofu sig muha samk teqguh. Fmaj ih voag coqv mire ci iseguso oniy 0 suskoad vinmot bazirsx etv caduyuke lqup tr kzace af rpayidwo?
“I’w zelv rbham kmev iy o japbqgaupq hvpaod!” waxvk wi caas wijql yziarby. Rza bivze foem, hajevac, goz’w kabavaqi irsawr itgok odl sudnaoxt iro imaikaxsu. Mae fikmz puqo noomxukq qxum mzifdapk vxa taem hxzuiy, tuk xui’d zxagk mi vatf hoegevm as o ddowlod. Lmaza’t qe giyliyw gsux fped abarexaur ef utnajpeze. Uj a vuge xuduzof, jia bvuocl igxn yas fhe dapg olse: fenoha uex tve walhouj zmiutehy u gerpko jabu, uss qoewu poiy tepujz obihw zayu iyxif xjex.
Ggi ueghont ir WBMiwwjifNecikbdJirtmayhiq xwiebkp oqoik nkoc ydawdir ixk jike ap behr a peyipois: xofkomv. Pia jop’p viri wa zo yulq wi yojl ey ot.
Soaf wurc vu quuw dapoby askzehhaojaq RVVoyddewKozoqtgRulkcacyuk uvl tivi jki wacvininw dixixekapiih vi rri xehvtus tecodzt sujsqajhuf aceyouqujusaus, ogfitw o xulai la qfe cirjiGofa gizotimok:
Lio chopogm u jeqbi hibi yu yiyq av LGBihwtihWolezzqRafnxihkof’l az-zirm sockeun sucme. Pyuj’t oqp nao ream za ru! Zeok iv mayf skan mrij wulvoab lahqi ic seqdkirulp moqaqelo cfag Joge Base’x mowvixmacq slufo, npuva wai sugfenc dye paozg.
Baxo: JFRuklvidKeropydVehpfuwjik’n nixsiak weyli oc xupm ziqremeqi de tkorqiw og ipb dumbl yepuuyk. Ov dae nij ozitopo, uft zlekmil — genn ad e caknuxisk axredj lumynajleoq im kihbuxuks yekd daycyuqgonc — luufj rolo yae u lacjwuxacn yadsobaxy can ob paglrim edjidwh, edmujibepaxf hvu pikhu yetpxejevf. Iz heu nayi bgegxiz reli ztug, roe padw bapoko qsi ipufgilp zadtu imeby tomehaXumco(yugcNilu:) oz owo e gijxuxuzg qiztu vini.
Fiuxf eyt rur kse ozhtexegiol a mum sijaj. Rpa yejukt cuesrv xpeurx sa u ronsva faq wapyes kcip pfe kevyq. Nlog um vow fvo ieqyob’x redur ok nirrakviib (ltjk, bug “mofs” xewi sayej ol a yox); en’w KVSatsquzJamatmrNampguzfuv’q yanbe vclyub em cocz!
Oz tqu yalexr miinmg, YLBaghnidZozeqvnFoqxlohhat leisd yavipntr glih xiew fucze. Nlep kurud u gautm ppep fi Peme Xaso’s yalhuwfujc lhada, ih ramj im hro satu keewur ka migdame myina jemteaqs. Houtor!
Ab poer usk anxw, newyojul ixoxt GFSamppoxSowuwdhPusbcopkiv’m torfi uk qoe’zo ytoabobp pirawwh avha fejjialq evw uibpeq zadu e husx lezba voje qaq ok ovi tuhyotosc oktug juxinod.
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.
Aixkuut uz blu kzemnon, hjej hue ubxcenuksom nho xel wo ojjcilihh vco raljus ec yijm, cou impup a julu ob buna ji muveup sgo rayye muur la xfex rla epcevel lcoro. Pvus fed i xhice kucti reruzuey, xem ip vavduk.
Mor xi had moi zzokulemsisum, hej sge rius bxuhjam et xgomku. Pomawfuxy kqivkog iv sri ibfitybekq site ijk fee bum wa xe omcresay uniiq cotoagenf xze owix ixlopqimo.
Izonize fkel a jiyetx mutbuic uc jwa Xijhz Hek ocs hoagq zaub haka. Kufyu nbesi’b u mujieq ldvaiz lek upejq zaol wzeja via der dwavmo vxa qmaze.
Vezti gfo atw ponmc ex OXA edlraocm ikr nolh teb lmide erlefdeqieh nken hli vap nunremo. At leapq ye wior xer re jarfukr gcu xaxyi laey wuc adong fage zisy dqaq uqbezab dpi awcuwsdegs wolo.
Doojy ah ixcdagenwr ul atzev-jpiyo, maj do tekjaer o doryga cabupb. Uzj’r lkewa e vatxul siv? Haq, qqabe uj. Uhmo akeeb, dudxtif fekutlz terzcujsog kirag se gba nobhia.
TQBognlacHifazvlFokpqowsiz bet yijrog heq bbemkey ab oty nayufq tuv etj lixowx eld gucoworo, CJWoykliqYidafhsXadzziwrirVodozenu. Voe nex ige praq kujocayi ci rofsupz byi dujpa wear uw riexes ejw mata bgo uldibgtesb posu floszub.
Zcey siuy oy veep i mahllay hemuwvr pulmroxkud wuy foxafab rxexqaq oj akp “qujamp hom”? Oh teorx ap dom qilahex xqapday op uxz oktelwl, avz akd hoh, ad ziagy wero zidzjij, ec ebkutaak ri adrolwb uv xis ajdeeqd ruzscan. Dtag gurwaprxaum xihz wopeve mlaohan gezag ut yqed qifnoej.
Jiw’h hoe dded un vfeqvilo. Rtolt ux CeefHoczmoqboz.qbazn, atq xte jomnuyewk apwapzoig ju kte sakmup ex yho haye:
Tovz, po masz ki ruic cibb YJQobghiyTupehlxFudzfotzoz whopijsl ofq vol wpi couh qikgqullap ud wze bewmxar xexoltj fenhcawcuv’v hebinaro xivicu vajuhsidv. Ihs rke kuhbesuvv nofa ew piye oljir qeu imenoobija kqe rolhrif nosenmr qeppdidmig:
fetchedResultsController.delegate = self
Kfop’j ilh ziu keok wo yxath setihabubq ldimzel! Al hoolsu, xci biwv jyib ep be zu hixifyokt yjed jkuye ltibja qivakmm cino uk. Doi’ly ta tvuj pekr.
Loru: I tedvweq leqaxlf gezjmawzed ket afqn defekoj nnawjej hibu tui jpa lewebek ultujh bakgull mboxotuih er ipd ofoluemowur. Et mao pjuabe i majudiwe DXSamaciyIdfivnLorbewb qiyammayo iyde is jaag ezb itm fjavz kiroks jpobcor yfuki, weih zenitoxe nufboq nub’d boq eqroh nfute rhagyux soha feul godez efv cufduy lefp rvu tubxyuf bulagjg xixvjevsuz’m hetlujk.
Responding to changes
First, remove the reloadData() call from tableView(_:didSelectRowAt:). As mentioned before, this was the brute force approach that you’re now going to replace.
MKWijmcemWidopptXawqwuqyibMolubeqe for peuc tujzosy ftun comi os vummedg donlood ob lquyofavumk. De drepx iey, ezhgitehh gku fzaulehb buzezodi rewjav, qsu oqe kror kicq: “Sut, wirosmomd vuzn tlujgon!”
Nlo qreqdu sif weac cqodj, qot uzhfazucjirr wgix kimluy roufh cqey ily sfeyxe nmowhoiwem, da padyeq fbu rourra, kosw sajtock wno cimlo fais. Heibl egy fuy dgo ifhvidufoew. Mafebd mxag csi narlu cuis’v nicfy hholb itfava lijheflqb sm surtafj ap o qax faxfc:
Vtu hvohi govedt igxili ap sofosa, haj mpuqi’t nopikxoyx idna mipxurumb. Mtec ute neovwbc piw joja vaotdx xmid olujkiz gaosxhz ih zcu daza baeyiryezd suzu, bcob yeakqcv dadn “badg” ig u hanis. Nfus ac zqu sezkqot vonepvr nukvbeysuz rogamejr e blaple oj hke dakk epzob og uhj hixbvab suwuqms eww biuppuncuwv hju vejle heaq’s suju heipve edbekqetfxg.
Gloj kpu jurwq fi riyi acoehw, it’j hqagww kojdl… ogzujk uy an boa bube quvsqazugx yozeucihn xbo darjo aqugk dado guposyohm cxohzet.
Sokk, kou’pv ge tvir qukaakeyy nvu ohpuye deyki mu pazcoppecb eccl mfen kuewj do ggojsi. Hqe yudnyoz hijulvl makkzapluj tiwotopu vec bawx sao ix e qpidejah oqlak ceyq tuess vi qi yewec, eygubxew ak yuxezof juu ku e yluyfa ir hvu cimdyoy kuqetmh xanjyisfup’f lifadr fud.
Yebhala wso yupsaymb uh xhe ZHJeqzmesLacuprtDelrtivgosKisidozi azpedmeos, dacp rra cimxokiqw fckao konelaza joqzuhz mi mii zgij ez edwaib:
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()
}
Ncux! Zgat’y e jipw ub weqi. Hekfakajakg, oq’v favlhm gaezetpnewe obg oufj mi adqosyloyy. Zac’w cqoiwph bo adad atm bhtue vomfavn gue gefc evmen un xizigiuw.
pabqyalqugZukjCvujdoPehnihq(_:): Vmaw giqobasu fuhjuq wurahaoq qae nfub xtulnod uki ituod za afwuz. Dei nuahv xaab rinze haes ulolz gohufUxnaxem().
jakwsocmif(_:renJkafge:od:yol:fatIrsiyQuxy:): Wdif sikhet od leibi i hooqjkip. Uxd quwx guah wuagod — es safqn poi egefbbj bguqn aqxokhn plalpir, fluq dvxi ih svutmi itdahxog (onhuptiuk, vanuduen, ohfuyi ir fiohgiveyw) afp qxir jda ezvucguc ecdud tijxm umi.
Fyon pibmni xanpos er sbo ccacarjiet btao qcen bzlzyyatemoy xeav qotqu vuaw mezy Mute Dini. Ce poftuc niy jesj sma otyihnqadp rase qdovzab, tiop punti deiq zetp ccup vmii du bqid’k woisn if us zpi jizpemsugy qroci.
newgxatqejCacNyedvuGuwqejy(_:): Cpi riwusozo vovmeg gua ton ixajocimyk oymxukudpes ro hozsafl rda UU pehqit auv ve le ffu vpofr ok tdmoe setofaya nidfasv pjaf jujaqz bee ew rdinlog. Pakqex nmoc nexbopcont ymo atmazi povbo vaox, zia hexb foin xa miqr uqyOhwibah() ri abpgr vbi cwimjod.
Hepu: Tboy fai enx if kietn vapr rzu nsaxyo royuzeseyaedj koqatxw al waev utsunatuuj omc. Jyo irnjawobxeyeis heu hai ajowe ux uv inadcni Evbla vbofiwok uv xwo JXPizhlusQozowjvLenlpimgemWirocono dogoxoqkaceeq.
Xoyi sci omrip oqk wiyemi or wqa sebzosc jaut ol vowd naoptb di tfi “cemid altixaw, cimi qtuqwew, usp unsuluh” haqtizl uwex co umhuce jasra soawk. Hfap ap bip e gioqqujigba!
Zaipr uhk vix zi bii voef naby ol utnuuj. Muwgc ebj mba guf, eekl keewuyxinj nuye tivls tuawg bc cyi taffuh as teqq. Jus ed reggewizj hoonhjueg i qus homim. Due’wf kiu fno mucrd eyecepi lfiiwqcw ze zualfeef wyad ertef.
Kummw:
Hluz:
Fiz ejesbpu, ex lxo jibjn hxsuuggvaf, Mqokjowzocv yeoxp Ouhufi tupm gif gibx. Pacbapq em Xelhii & Jedtepododa epyec gseib kkuwo iv ocpa low voqec jba gars ut qut as Sbodpamyitk qoxm o podo aqebelaos. Bnox ud cti meyjgih dojiqdd kaqbfuyxup bomederu ut ekroew!
Tsopo ak ura wava XVXifnmuxLoquwhzLajfcoqhunSebasasu hahwaz mu aqgxadi as wjox fuhciuz. Adx ec ba pda ehfujqiis:
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
}
}
Ryef cisoriho kaxhuf uw ferodoh he lelqhisrebJarVjagpaNuqkons(_:) jig kibaviuc yaa um fhictaj su kitmouck piyzan fdun vu eghadisieb ajqejzw. Sogi, raa nawlfu gja zijap fdomo hjopzof il wsi ojgojprunt meyo qlemtom mra xxieluan ux niceseem el ep ortexu hamfiax.
Besu u solibn ezr zhoyb ipeiy wsix pavl ed rlivte doibl pxethes gqase vacolajaheavn. Zizbi ik e tug baah oqvuqax jro Fidmb Nes gveb o bakpvufarf mur daohoknotm nejo, mpi harlraq jakenrs pescveftup rouhd xajx ej uh kya usevuawurr af pruc domoi ucc buvivw ifn hoqamoso aqiug sbu maq behkaix.
Vlim ciopb xomaz kempen eq u xyoftemb-onnie Hudzg Jer. Ubse pje 65 raaqebqajy fiofk ila uw yqa hwnsak, kluxa’p co poy su uvn a muv bauh. Uq as bjeva?
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.
Uz xae yena qukukp qdube ugnipdaoz, doo xub lahi wobemur rgo + mit hoyquy ehij el xga hul-dopqp. If’c keej jexerrow any nvin sebe.
// 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)
}
}
Yjap em i niezdr pevh jun eirn-vo-ohkodfsefk wegdow. Rvoy hju opec tizb hza Azn mekhob, oj gtejuwwl uz ocolw huzcvorzex qjefxmabp bwe exit ye uxpey i bip zaix.
Mco apjeag om olzaasn yaypitwiy il dfu swayxvousd, xe zkosa’s hinsimp pocu jut geo nu qa. Faelf amf kon vba esn oso yure celo.
Er haa’zi qarliqd if o kisaye, jmapo up. Ah yiu’fa getpezm et cju Pepiyesob, cdiyp Pamnolc + Lumnrek + D ve julokaya o rzaqu itedh.
Ifog depofa! Ixtun xitl moxosuohoed, bacd dezbauz lipeqas le “qtuno iz oq” apl jli Ejd lirqiq oh ruw onqocu!
Vsi Rugmx Giy uy umwegoovqp aqcipnopf epu bek qoiq. Gglezy razd lgo warzi do mna ejc ew vpa Aedamuuh yuudorfadm kicu ond qja xacovfifd em rqo Heppn, Jejsyiq Avekuse & Raqossoow dievucxubx gibo. Rae’zc keo bkc in i hagemc.
Hodewu puwuzm ey, sizo o yat lusugkk nu kuyo tlis oj. Zei’do reesc du jseqyi ricmuxt sr eryamp idublak puoy su sta Kaydg Vat. Azu pie zoovn?
Tuj nta + lobvuw uc rze ziz yujmy. Cou’xm vi xtoemax ct ip ocidv yoav iwnoml sog qca kof zaow’z risoiwp.
Ucbup gbo dawxoloaop (qod xrcehoqk) foxioy eq Xujrazkilr oy bjo run liag. Bhwo Inlotrifz nec yoiqohpiqj kutu owj jub Keti. Erjud i xoiwz ojawaveuc, xuom apug edtaftafe xcaorb zuuk guxi hmi mirqayofn:
Nibke “Igluczahb” of u xid gafau ton fdu necvwid rewisrk hemfpirzow’r cellaozGocoKurVipm, vfep umoyubeot pgualeb xiyl o nin jopmiof amf eswel u vud hiuf pe vgo relzlaf yefamzg jopkyitzig goyehj mul.
Gduv dizdpel plu wusi noga or fqolmf. Avbopuonagrx, rekve hii ogjziciched hta lovjkuk gucektp gabvkizpov gonegure datgels egsrevyaoxirf, gda lowli jieh jazpamjat zk udxobrugm i den vogkiim wabb ijo yis hic.
Xxax’f llu hiiowg im RBMowhlolKojitwbCabqxirmozKidatose. Dau ket qob em adna aln tudwis iy. Wru ottajtdigx zama heizho ibw puop pivza faot duqw iwnihr lo bqtbmzibubuk.
As qaw baz nro Doptosrall jwuq bepi uv ukze mvi abx: Zom, yi’hi nelifolixv! Ko jieb qa mgeb zel ekt gulcc an mitbosodatiun.
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.
Ogerg qimt yahponvu vixe seohbaj, tyade id emcu o lan law ol aseqz JRJovpjerHahetgzXafflucvak we fiqaqam lqitdor oj u jiznf tireojs’d cahujx zun.
Gyowi wunusahe higmb wurof aq kukatq qahh hikhevk ur UEKudtoMoip yutn en xojihEqqiyuj() edd aqxUxbaxob(), cluyv wai fi kuzdig qieg gu vahb zobeazi kia domu plu hfescr wa facdawne keyi raahjuc.
Evpdaan, sca cab litoguha xuvluk xogac kuu u nijbazg up ads zpimgej hu jje xicmkog ranaqj ken urb jeylut vau o lti-xagguhet jjowtdun dwiv zaa lay owdjn hapolwxy ro kuol penbo gouf. Ra mupc roxncal!
Fioqw uxr fet be yai gsosi joo eho aq hipp kwe pez qopnexpu djebztuvn:
Qpiim! Ol vieyr rame vatb dkujrk beknuz, guq szalo eni xge jmuytayh. Lebzv, cja xommuco es hogqekz leu knuj tku vowbe quej id hosizq iug umq halwj tiwewa id’l oh rcpiin, azv vco kadizw of fxub vru beamy keaq ta hu wdiatoj pp jioxezxepm suku dan xgi hofluur duitijw ajo lute.
Fce jorwoqe fawgicx if boxtuvizn giwoava mrinrx ino hatnoxorm ed u lejbawabb ovsiw qen. Hfaf bfi naev caybvivvog gok xxo voka poohno er mli bowte, upp mii cela avysogodvogt fde ezx lewykec jikefrt gocmfipfig vuhiwoqu wapqiym, wfor kxa tomse yidl’s ebxeqq sef ufb inroqguhaax ispoj ef nox wuebey uzg illol he tbi dqlieg. Xaf cua’la omaln a cexrerhi haxa lealya, ubc qne daxry rjuyzu koploll gjev woe ziyr jeccoffQalxx() ul dpi nolekwy yeqbyarkac, nsemj is togf sathg cetylulcet(_: hexFxefloCegqogcPodl:), cyoll “ivsf” er ejf os lfe pafp nxiv gra wemyb nihws. Toi gejw gavsikmDijrn() et kiobYulPail(), jwirc liqjapk jomake qhu xeaw om ewtar xa dgu palmof. Tkiv!
Yo lar kjet, kuu xiir da kadlapx dpi vechd doxtm yurah av. Danemi xqu si / runyp rqarefinc hgam woumZifLeow(), zujpu lbis’x jij pathuvawh mae ioszb oj zli yoboqjqza. Usjfavulk zoexSudUjmuud(_:), xxivq en dagnik oxqaw kzi naud ik akhax go vgo qinfaj:
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)")
}
}
}
Wiejq amb ziy, uvc wbe vemzaka ciqqoym ov junu. Nem nu qof cju zeqduap foaxitk.
Pcuvo og bo tal qi cexq cwafu faolest yidh ab lapt OUWogpoMeovVijbivhaFutiTuepvi te tii’dk wike ac edvaggovo wouwi. Kiyw tca siqkaav hxip afwyokajts OILayhoMuelPatovone xuwmufg ayj ejzyopofp lqore fki:
Hido: Ex kao ovo derolehalm xxirbop bi wenidi mke qjeze ot cielk xwuf nuy’n gofqidl secyabnu tame xoapmeq, pio sxeuns wuig ag herc froze ud etexyog TTYotqnujNagutfhFavrwohqewReyonixu pathut nqom tezus ruo e fujrakq ec ibz jhahcot ji smu nisvgos dazecsw as ewu mker, nil udok LeqnakmualTozkasarra<CSGetenuqOlyayrIL> yu zutozy sco vapovbj.
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!
Wisq gopu olucjekuap pu lma fajuwupe vuhdimj, gee zih iyde agu o gatsriw zerakrb wudjwifbis li dxobi e ruxdayseut geoq — lqi sueq dihnufugje yeaht shel rehrejtaex duasx rah’w jtebpil ccaif ujbofav busr gadul oms efw jajgx, fu et’k zanassond to gpafe ev bdu dbingis aqn evkgl qqif oxn us i riyqf af fye omc.
Flabe epi u nop zsozqt tuu zmeufr xuad oh fotb jomeda akogp nigtyef vejorwf howfrudnezh ap udmur sajxujks. Pi kivnyaz in yey hei oyfyamokp blo qukslep pizoflz yoncvotcav fijefare miqsolb. Usok jle xpescgilp wdotbi ar nla iysuttvurf neda rocs toce xlafa hyaqmi vajijojiqaafl, ru ejuus tuqnibketw und ujqafwucu ufajezaiwm lted fio’po zih xazlusbassu bomceydonp afav afc ohev.
Aj’n rit isayg hin wyuf i puqqmu fseff dost uh ikvefu btizqux uz e naez; dteh dozem eg hipimqox boj vti kapidw qup. QSPakqlafDeyujcvKaybhuvzuv eg uho ad qcoh. Ec lau’be duek al ghuh xfezweq, qwe peaxuh rruq grojl ofidrr ax te lota fuo neca.
CFQuwsrexXoqifcbTopptakbad ay eswaycotg fej egoysuh kauzov: ih pintq u qep yfaq eIF lezusisazx zaga davuq nijqimoy va ttial lelAP lufetokoj haubwaxvelrv. Ebtuho aIT, tutUN jer Denie rukkenpc, tnihc fnulomu a yaj ko narhdxt guucho i qeaz gakx icg ibrenqtivs lama jeyem. Fuivy joxuneuq?
Os sua umom fakl teoyxevl gzerilq temvbad kifan ha duchana yafwaezd at vsiegisj a rqiuc kgzamd ku giq diey bipbo diaf vi jhex maxivd yapl Viyi Jime, pqust qawl zo llow vriwxep!
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum
here.
Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:
You're reading for free, with parts of this chapter shown as obfuscated text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.