ios - Ensuring that FetchedResultsController matches its linked TableView after MOC save -
tldr - frc appears out of sync table view linked to. how can sync?
my view controller (vc) has table view called holestable
managed fetched results controller (frc) called fetchedresultscontroller
.
when user presses save button on vc, calls ibaction
called saveholes
. saves data in frc's managed object context tests data rules have been followed. errors caught , message sent on vc displays error user after unwind (not shown here).
i'm discovering frc doesn't have same contents on-screen holestable
because function lookforsiproblems
doesn't pick errors when called. however, able validate underlying database has received , stored data that's on screen.
subsequent calls vc show saved data , subsequent presses of save button will find error problems.
so, appears frc results out of sync shown in holestable
@ time validation.
here's salient code:
fileprivate lazy var fetchedresultscontroller: nsfetchedresultscontroller<hole> = { // create fetch request let fetchrequest: nsfetchrequest<hole> = hole.fetchrequest() // configure fetch request self.teecolourstring = self.scorecard?.value(forkey: "teecolour") as! string? fetchrequest.predicate = nspredicate(format: "%k == %@ , %k == %@", "appearson.offeredat.name", self.coursename!, "appearson.teecolour", self.teecolourstring!) fetchrequest.sortdescriptors = [nssortdescriptor(key: "holenumber", ascending: true)] // create fetched results controller let fetchedresultscontroller = nsfetchedresultscontroller(fetchrequest: fetchrequest, managedobjectcontext: self.coredatamanager.mainmanagedobjectcontext, sectionnamekeypath: nil, cachename: nil) // configure fetched results controller fetchedresultscontroller.delegate = self return fetchedresultscontroller }() @ibaction func saveholes(_ sender: any) { print("prepare save") { try fetchedresultscontroller.managedobjectcontext.save() } catch let error nserror { print("could not save. \(error), \(error.userinfo)") } lookforsiproblems(holes: fetchedresultscontroller.fetchedobjects!) } func lookforsiproblems(holes: [hole]) { // takes fetched result set of 18 records , looks duplicate or missing si values // should not possible have duplicate hole numbers, check done see if si number has been seen before and, if so, other hole. var siexists: [bool] = [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false] var sitocheck: int let maxhole = 18 var currenthole = 0 var proceed = true while currenthole < maxhole && proceed { sitocheck = int(holes[currenthole].strokeindex) if sitocheck == 0 { // can happen when scorecard has not been completed messagestopassback = ["incomplete scorecard", "not holes have been given valid stroke index. changes have been saved cannot enter player scores until error has been fixed."] flagincompleteparforcourse(errorcode: 0) proceed = false } else if !siexists[sitocheck-1] { // no si hole number has yet appeared, set , carry on siexists[sitocheck-1] = true currenthole += 1 } else { // si has been seen messagestopassback = ["duplicate stroke index", "stroke index \(sitocheck) has been duplicated. changes have been saved cannot enter player scores until error has been fixed."] flagincompleteparforcourse(errorcode: 1) proceed = false } } }
edit #1 -
in response comment @agrizzo, full frc delegate methods shown below.
these appear working , carbon copy of delegate code i've used elsewhere in app.
extension scorecardviewcontroller: nsfetchedresultscontrollerdelegate { func controllerwillchangecontent(_ controller: nsfetchedresultscontroller<nsfetchrequestresult>) { holestable.beginupdates() } func controllerdidchangecontent(_ controller: nsfetchedresultscontroller<nsfetchrequestresult>) { holestable.endupdates() } func controller(_ controller: nsfetchedresultscontroller<nsfetchrequestresult>, didchange anobject: any, @ indexpath: indexpath?, type: nsfetchedresultschangetype, newindexpath: indexpath?) { switch type { case .update: holestable.reloadrows(at: [indexpath!], with: .automatic) case .insert: holestable.insertrows(at: [newindexpath!], with: .automatic) case .delete: holestable.deleterows(at: [indexpath!], with: .automatic) case .move: holestable.moverow(at: indexpath! indexpath, to: newindexpath! indexpath) } } func controller(_ controller: nsfetchedresultscontroller<nsfetchrequestresult>, didchange sectioninfo: nsfetchedresultssectioninfo, atsectionindex sectionindex: int, type: nsfetchedresultschangetype) { switch type { case .insert: holestable.insertsections(nsindexset(index: sectionindex) indexset, with: .fade) case .delete: holestable.deletesections(nsindexset(index: sectionindex) indexset, with: .fade) case .move: break case .update: break } } }
edit #2:
to see if frc updating on each edit, added call lookforsiproblems
frc delegate methods thus:
func controller(_ controller: nsfetchedresultscontroller<nsfetchrequestresult>, didchange anobject: any, @ indexpath: indexpath?, type: nsfetchedresultschangetype, newindexpath: indexpath?) { switch type { case .update: holestable.reloadrows(at: [indexpath!], with: .automatic) lookforsiproblems(fetchedresultscontroller.fetchedobjects!) case .insert: holestable.insertrows(at: [newindexpath!], with: .automatic) case .delete: holestable.deleterows(at: [indexpath!], with: .automatic) case .move: holestable.moverow(at: indexpath! indexpath, to: newindexpath! indexpath) } }
and result frc is showing values in sync table. went on isolate think problem is. if edit fetched object in table hit save button, frc in lookforsiproblems
seems using 'dirty' data. however, if edit object in table click on item in table, say, frc in lookforsiproblems
uses 'fresh' data.
not sure means.
wiki
Comments
Post a Comment