tabelview RowInSection fatal error swift - ios

I'm a beginner in IOS development in swift. The problem I am facing is: I am building an app using CoreData and the app contains table view and table cell. I can't really explain because of my lack of knowledge so I'm sharing screenshots. I have seen other Questions asked, none of them solved my error. and I have also made a function for context in AppDelegate which is
#available(iOS 10.0, *)
let ad = UIApplication.shared.delegate as! AppDelegate
#available(iOS 10.0, *)
let context = ad.persistentContainer.viewContext
my code for VC is
import UIKit
import CoreData
class MainVC: UIViewController, UITableViewDelegate, UITableViewDataSource, NSFetchedResultsControllerDelegate {
#IBOutlet weak var tableViewmain: UITableView!
#IBOutlet weak var topSegment: UISegmentedControl!
var fetchResultControll: NSFetchedResultsController<Items>!
override func viewDidLoad() {
super.viewDidLoad()
tableViewmain.delegate = self
tableViewmain.dataSource = self
doFetch()
}
func configureCell (cell: ItemsCell, indexPath: IndexPath) {
let item = fetchResultControll.object(at: indexPath) // remember as here
cell.confugringCell(item: item)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableViewmain.dequeueReusableCell(withIdentifier: "ItemsCell", for: indexPath) as! ItemsCell
configureCell(cell: cell, indexPath: indexPath)
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let sections = fetchResultControll.sections{
let sectionInfo = sections[section]
return sectionInfo.numberOfObjects
}
return 0
}
func numberOfSections(in tableView: UITableView) -> Int {
if let allSections = fetchResultControll.sections {
return allSections.count
}
return 0
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 150
}
func doFetch() {
let fetchRequest: NSFetchRequest<Items> = Items.fetchRequest()
let dateSrot = NSSortDescriptor(key: "created", ascending: false)
fetchRequest.sortDescriptors = [dateSrot]
if #available(iOS 10.0, *) {
let controller = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
do {
try controller.performFetch()
}
catch {
let err = error as NSError
print("\(err)")
}
} else {
// Fallback on earlier versions
}
}
//controler willchnge
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableViewmain.beginUpdates()
}
//controlerdidchnge
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableViewmain.endUpdates()
}
//controlerdidchangeanobject
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch(type) {
case .insert:
if let indexpath = newIndexPath {
tableViewmain.insertRows(at: [indexpath], with: .fade)
}
break
case .delete:
if let indexpath = indexPath {
tableViewmain.deleteRows(at: [indexpath], with: .fade)
}
break
case .update:
if let indexpath = indexPath {
let cell = tableViewmain.cellForRow(at: indexpath) as! ItemsCell
configureCell(cell: cell, indexPath: indexpath) // as used here
}
break
case .move:
if let indexpath = indexPath {
tableViewmain.deleteRows(at: [indexpath], with: .fade)
}
if let indexpath = newIndexPath {
tableViewmain.insertRows(at: [indexpath], with: .fade)
}
break
}
}
I hope you understand me.. Any Help would be highly appreciated

Replace following line with your .
var fetchResultControll: NSFetchedResultsController<Items>?

Related

NSFetchedResultsController doesn't respond to changes in the number of sections

I'm getting the following error when I delete a cell from the table view:
Invalid update: invalid number of sections. The number of sections
contained in the table view after the update (0) must be equal to the
number of sections contained in the table view before the update (1),
plus or minus the number of sections inserted or deleted (0 inserted,
0 deleted)
I'm using the NSFetchedResultsControllerDelegate to respond to the changes in the number of section. I'm retrieving the data from a plist file in Core Data and each addition of the table view cell increases the number of sections accordingly, so 3 cells = 3 sections.
override func numberOfSections(in tableView: UITableView) -> Int {
return fetchedResultsController.sections?.count ?? 0
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let sectionInfo = fetchedResultsController.sections![section]
return sectionInfo.numberOfObjects
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let goal = fetchedResultsController.object(at: indexPath)
cell.textLabel!.text = goal.title
return cell
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let goal = fetchedResultsController.object(at: indexPath)
self.context.delete(goal)
self.saveContext()
}
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .delete:
tableView.deleteRows(at: [indexPath!], with: .automatic)
default:
break
}
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
let section = IndexSet(integer: sectionIndex)
switch type {
case .delete:
tableView.deleteSections(section, with: .automatic)
default:
break
}
}
context is this:
var context : NSManagedObjectContext {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
}
How I load the saved Core Data onto FRC:
func loadSavedData() {
if fetchedResultsController == nil {
let request = Goal.createFetchRequest()
let sort = NSSortDescriptor(key: "date", ascending: false)
request.sortDescriptors = [sort]
request.fetchBatchSize = 20
fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: self.context, sectionNameKeyPath: "title", cacheName: nil)
fetchedResultsController.delegate = self
}
fetchedResultsController.fetchRequest.predicate = goalPredicate
do {
try fetchedResultsController.performFetch()
tableView.reloadData()
} catch {
print("Fetch failed")
}
}
try calling beginUpdates and endUpdates in these delegate apis also.
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates
}
if you still face issues then in this delegate function, try to call like this:
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.performBatchUpdates {
tableView.deleteRows(at: [indexPath], with: .automatic)
tableView.deleteSections(section, with: .automatic)
} completion: { (status) in
}
}

How do I get tableView.reloadData() to work properly with core data?

When I run the app, I am able to populate the cells in my tableview but when I save (from a separate view controller) and go back to the tableview controller, tableView.reloadData() gets called but nothing happens. I use the notification center to reload the data before popping back.
TableViewController.swift:
lazy var fetchedResultsController: NSFetchedResultsController<Pet> = {
let fetchRequest = PersistenceManager.shared.fetchRequest()
let context = PersistenceManager.shared.context
let frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
return frc as! NSFetchedResultsController<Pet>
}()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(pushToAddPetViewController))
tableView.register(MainTableViewCell.self, forCellReuseIdentifier: "cell")
do {
try fetchedResultsController.performFetch()
tableView.reloadData()
} catch let err {
print(err)
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(loadList), name: NSNotification.Name(rawValue: "load"), object: nil)
}
#objc func loadList(notification: NSNotification){
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
AddPetVC.swift:
func saveContext() {
// Inputs saved to coreData
PersistenceManager.shared.saveContext()
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
self.navigationController?.popViewController(animated: true)
}
You fetched data in viewDidLoad() which will not be called when you come back to the view. You should fetch data in viewWillAppear() and reload from that or from the notification observer method loadList(notification: NSNotification) . So, add the below code in viewWillAppear() or loadList(notification: NSNotification) :
do {
try fetchedResultsController.performFetch()
tableView.reloadData()
} catch let err {
print(err)
}
When you popViewController to call back to previous ViewController, viewWillAppear() will not be called.
Create fetchedResultsController instance and conform to NSFetchedResultsControllerDelegate. NSFetchedResultsControllerDelegate will observe the changes and reflect changes on UI
lazy var fetchedResultsController: NSFetchedResultsController<Pet> = {
let fetchRequest = PersistenceManager.shared.fetchRequest()
let context = PersistenceManager.shared.context
let frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
frc.delegate = self
return frc as! NSFetchedResultsController<Pet>
}()
In your viewDidLoad
do {
try fetchedResultsController.performFetch()
} catch {
let fetchError = error as NSError
print("Unable to Save Note")
print("\(fetchError), \(fetchError.localizedDescription)")
}
Now implement NSFetchedResultsControllerDelegate
extension YourController: NSFetchedResultsControllerDelegate {
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
uiTableView.beginUpdates()
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
uiTableView.endUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch (type) {
case .insert:
if let indexPath = newIndexPath {
uiTableView.insertRows(at: [indexPath], with: .fade)
}
break;
case .delete:
if let indexPath = indexPath {
uiTableView.deleteRows(at: [indexPath], with: .fade)
}
break;
case .update:
if let indexPath = indexPath, let cell = uiTableView.cellForRow(at: indexPath) as? UserCell {
configureCell(cell, at: indexPath)
}
break;
case .move:
if let indexPath = indexPath {
uiTableView.deleteRows(at: [indexPath], with: .fade)
}
if let newIndexPath = newIndexPath {
uiTableView.insertRows(at: [newIndexPath], with: .fade)
}
break;
}
}
}
Implement UITableViewDataSource & UITableViewDelegate
extension YourController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let sections = fetchedResultsController.sections else {
return 0
}
let sectionInfo = sections[section]
return sectionInfo.numberOfObjects
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PetCell", for: indexPath) as! Pet
configureCell(cell, at: indexPath)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let pet = fetchedResultsController.object(at: indexPath)
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == .delete) {
// handle delete (by removing the data from your array and updating the tableview)
//let user = viewModel.user(for: indexPath.row)
let pet = fetchedResultsController.object(at: indexPath)
//Delete pet and reload table
}
}
func configureCell(_ cell: Pet, at indexPath: IndexPath) {
let pet = fetchedResultsController.object(at: indexPath)
}
For more details and code follow the demo project
https://github.com/rheyansh/CoreDataDemo

UITableView unexpectedly bounces with beginUpdates()/endUpdates()/performBatchUpdates()

I have a pretty straight forward UITableViewController /NSFetchedResultsController case here. It's from Xcode Master-Detail App sample code, So easy to reproduce.
I have a CoreData Model with 1 Entity with 1 String Attribute. It's displayed in a UITableViewController. I use .subtitle system cell type.
By selecting a row, I simply update the String Attribute.
So my problem is, when I add just enough rows for the tableview to scroll (10-11 rows on a iPhone 5s with navbar), and I scroll down, and select any row, the tableview bounces up and down.
If there is less rows (less than 10 rows), or more rows (12 rows and more), the behaviour is normal.
So It seems to be at the limit of the scroll view the problem happens.
If I don't use beginUpdates()/endUpdates(), the problem goes away, but I loose their advantages.
Here is a video link of what happens
class TableViewController: UITableViewController, NSFetchedResultsControllerDelegate {
var managedObjectContext: NSManagedObjectContext? = nil
#objc func insertNewObject(_ sender: Any) {
let context = self.fetchedResultsController.managedObjectContext
let newEvent = Event(context: context)
newEvent.aString = "a String"
try? context.save()
}
override func viewDidLoad() {
super.viewDidLoad()
let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(insertNewObject(_:)))
navigationItem.rightBarButtonItem = addButton
}
override func numberOfSections(in tableView: UITableView) -> Int {
return fetchedResultsController.sections?.count ?? 0
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let sectionInfo = fetchedResultsController.sections![section]
return sectionInfo.numberOfObjects
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let event = fetchedResultsController.object(at: indexPath)
configureCell(cell, withEvent: event)
return cell
}
func configureCell(_ cell: UITableViewCell, withEvent event: Event) {
cell.textLabel?.text = event.aString
cell.detailTextLabel?.text = event.aString
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let event: Event = self.fetchedResultsController.object(at: indexPath)
event.aString = event.aString! + ""
}
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let context = fetchedResultsController.managedObjectContext
context.delete(fetchedResultsController.object(at: indexPath))
try? context.save()
}
}
var fetchedResultsController: NSFetchedResultsController<Event> {
if _fetchedResultsController != nil {
return _fetchedResultsController!
}
let fetchRequest: NSFetchRequest<Event> = Event.fetchRequest()
fetchRequest.fetchBatchSize = 20
let sortDescriptor = NSSortDescriptor(keyPath: \Event.aString, ascending: false)
fetchRequest.sortDescriptors = [sortDescriptor]
let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext!, sectionNameKeyPath: nil, cacheName: nil)
aFetchedResultsController.delegate = self
_fetchedResultsController = aFetchedResultsController
try? _fetchedResultsController!.performFetch()
return _fetchedResultsController!
}
var _fetchedResultsController: NSFetchedResultsController<Event>? = nil
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
switch type {
case .insert:
tableView.insertSections(IndexSet(integer: sectionIndex), with: .automatic)
case .delete:
tableView.deleteSections(IndexSet(integer: sectionIndex), with: .automatic)
default:
return
}
}
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:
configureCell(tableView.cellForRow(at: indexPath!)!, withEvent: anObject as! Event)
case .move:
configureCell(tableView.cellForRow(at: indexPath!)!, withEvent: anObject as! Event)
tableView.moveRow(at: indexPath!, to: newIndexPath!)
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
}
}
I was running into the same problem, and I found that implementing the estimatedHeightFor... methods fixed the issue for me. The issue seems to stem from using automatic cell heights in the table instead of explicitly defined heights per row.
The table I'm using has both rows and section header/footers, so I needed to define estimated heights for both rows and headers, and this solved the strange bouncing animation during batch updates.
Note that returning 0 for section header heights will use the table view's default value which is likely UITableViewAutomaticDimension, so return a positive number for the estimated heights.
My code in Obj-C:
-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 44; // anything except 0 or UITableViewAutomaticDimension
}
-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForHeaderInSection:(NSInteger)section{
return 18; // anything except 0 or UITableViewAutomaticDimension
}
-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForFooterInSection:(NSInteger)section{
return 18; // anything except 0 or UITableViewAutomaticDimension
}

crash in core data xcode 8

i am facing an issue in IOS swift Xcode 8
after i setup my Core Data and before i insert any junk data in purpose of testing the fetch function the app run correctly with no crash and no data but after i insert a data the app crash with below message
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<UITableViewCell 0x7f9b26054c00> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key Deatials.
here is my code
//
import UIKit
import CoreData
// the NSFetchedResultsControllerDelegate needed to start woring in all the function for datacore
class MainVC: UIViewController , UITableViewDelegate, UITableViewDataSource,NSFetchedResultsControllerDelegate{
// definning the main view the table and the segment.
#IBOutlet weak var TableView: UITableView!
#IBOutlet weak var segment: UISegmentedControl!
// need to difine the NSFetchedResultsController to define the remaining 3 functions for the tableview
var controller : NSFetchedResultsController<Item>!
override func viewDidLoad() {
super.viewDidLoad()
generateTeseData()
attemptFetch()
TableView.dataSource = self
TableView.delegate = self
}
// we need to define 3 main fucntions for the table view
func numberOfSections(in tableView: UITableView) -> Int {
if let sections = controller.sections{
return sections.count
}
return 0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// need to difine number of rows in section by NSFetchedResultsController
if let sections = controller.sections{
let sectionInfo = sections[section]
return sectionInfo.numberOfObjects
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// linking the cell var with the class of itemCell been created in the View Folder
let cell = tableView.dequeueReusableCell(withIdentifier: "CellItem", for: indexPath) as! ItemCell
// function been created below:
configureCell(cell: cell, indexPath: indexPath as NSIndexPath)
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 150
}
func configureCell (cell:ItemCell, indexPath: NSIndexPath){
let item = controller.object(at: indexPath as IndexPath)
cell.ConfigureCellsInCellclass(item: item)
}
func attemptFetch () {
let fetchrequest:NSFetchRequest<Item> = Item.fetchRequest()
let datesort = NSSortDescriptor(key: "created", ascending: false)
fetchrequest.sortDescriptors = [datesort]
let controller = NSFetchedResultsController(fetchRequest: fetchrequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
self.controller = controller
do{
try controller.performFetch()
}catch{
let error = error as NSError
print("\(error)")
}
}
// these two function for the update in the tableview
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
TableView.beginUpdates()
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
TableView.endUpdates()
}
//this function to preforme all the functions for the <Data Core>
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch(type)
{
case.insert:
if let indexpath = newIndexPath {
TableView.insertRows(at: [indexpath], with: .fade)
}
break
case.delete:
if let indexpath = indexPath{
TableView.deleteRows(at: [indexpath], with: .fade)
}
break
case.update:
if let indexpath = indexPath {
let cell = TableView.cellForRow(at: indexpath) as! ItemCell
configureCell(cell: cell, indexPath: indexPath! as NSIndexPath)
}
break
case.move:
if let indexpath = indexPath {
TableView.deleteRows(at: [indexpath], with: .fade)
}
if let indexpath = indexPath {
TableView.insertRows(at: [indexpath], with: .fade)
}
break
}
}
at this point the system will cun without any crash
but after i add the insert function in below it's start crashing
func generateTeseData(){
let item = Item(context: context)
item.title = "IMAC"
item.price = 2000
item.details = "Soon"
}
this is my view cell file
class ItemCell: UITableViewCell {
// this view to take all the label from the view and link it here
#IBOutlet weak var thump: UIImageView!
#IBOutlet weak var Title: UILabel!
#IBOutlet weak var Price: UILabel!
#IBOutlet weak var Deatials: UILabel!
// using this function to set the values of the items with the labels been linked before in upper section
func ConfigureCellsInCellclass (item:Item){
Title.text = item.title
Price.text = ("$\(item.price)")
Deatials.text = item.details
}
}
thank you guys in advance
As #Larme says your problem is related to IBOutlet which is not reflect in class anymore.
Disconnect bad outlet

Swift, CoreData: TableView not reloading data when childVC dismissed

Ok I am brand new to this and am a bit overwhelmed going through many tutorials and articles. And spent a few hours sorting through similar issues with no luck in fixing my own. I have a "AddSiteVC" to allow the user to add or delete Items that are put into CoreData and then displayed in a TableView on my "MainVC". My problem is when I press save or delete and get dismissed back to my MainVC onBtnClick the TableView doesn't update until I leave the MainVC and then come back. I don't know what I'm doing wrong but can't seem to find anything that fixes this... I don't know where my problem is so I'll include most of my MainVC code for reference.
Any help would be greatly appreciated!
Thanks!
import UIKit
import CoreData
class SitesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, NSFetchedResultsControllerDelegate {
#IBOutlet weak var tableView: UITableView!
var controller: NSFetchedResultsController<Sites>!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
attemptFetch()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SitesCell", for: indexPath) as! SitesCell
configureCell(cell: cell, indexPath: indexPath as NSIndexPath)
return UITableViewCell()
}
func configureCell(cell: SitesCell, indexPath: NSIndexPath) {
let sites = controller.object(at: indexPath as IndexPath)
cell.configureCell(sites: sites)
cell.accessoryType = .detailButton
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "AddSiteViewController" {
if let destination = segue.destination as? AddSiteViewController {
if let site = sender as? Sites {
destination.siteToEdit = site
}
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let sections = controller.sections {
let sectionInfo = sections[section]
return sectionInfo.numberOfObjects
}
return 0
}
func numberOfSections(in tableView: UITableView) -> Int {
if let sections = controller.sections {
return sections.count
}
return 0
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 75
}
func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
if let objs = controller.fetchedObjects, objs.count > 0 {
let site = objs[indexPath.row]
performSegue(withIdentifier: "AddSiteViewController", sender: site)
}
}
func attemptFetch() {
let fetchRequest: NSFetchRequest<Sites> = Sites.fetchRequest()
let alphebaticalSort = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [alphebaticalSort]
let controller = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
controller.delegate = self
self.controller = controller
do {
try controller.performFetch()
} catch {
let error = error as NSError
print("\(error)")
}
}
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:
if let indexPath = newIndexPath {
tableView.insertRows(at: [indexPath], with: .fade)
}
break
case.delete:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .fade)
}
break
case.update:
if let indexPath = indexPath {
let cell = tableView.cellForRow(at: indexPath) as! SitesCell
configureCell(cell: cell, indexPath: indexPath as NSIndexPath)
}
break
case.move:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .fade)
}
if let indexPath = newIndexPath {
tableView.insertRows(at: [indexPath], with: .fade)
}
break
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
}
}
Please make following change in the code . Because viewDidLoad will be called when viewcontroller is loaded . But as per your requirement you adding something in Modal page. So you need move the code to viewWillAppear
import UIKit
import CoreData
class SitesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, NSFetchedResultsControllerDelegate {
#IBOutlet weak var tableView: UITableView!
var controller: NSFetchedResultsController<Sites>!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
attemptFetch()
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SitesCell", for: indexPath) as! SitesCell
configureCell(cell: cell, indexPath: indexPath as NSIndexPath)
return cell }

Resources