Swift - Fetching one to many data and displaying it in a tableview - ios

I have two entity:
swimingPool
Parameter
swimming pool has a one to many relation to parameter (each swimming pool has many parameter). I also have an inverse relation
I have saved my data and i fetch the swimming pool in order to display the parameters
Now i'm trying to display them in a tableview. Here's my code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ParamCell", for: indexPath) as! ParamCell
configureCell(cell: cell, indexPath: indexPath as NSIndexPath)
return cell
}
func configureCell(cell: ParamCell, indexPath: NSIndexPath) {
let swimmingPool = controller.object(at: indexPath as IndexPath)
let parameter = swimminPool.parameters?.allObjects
let paramArray = parameter
for param in paramArray! {
cell.configureCell(parameter: param as! Parameter)
}
}
and here's the number of rows in section
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let sections = controller.sections {
let sectionInfo = sections[section]
if sectionInfo.numberOfObjects > 0 {
return sectionInfo.numberOfObjects
} else {
let noDataLabel: UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: tableView.bounds.size.height))
noDataLabel.text = "insert a parameter"
noDataLabel.textColor = UIColor.black
noDataLabel.lineBreakMode = NSLineBreakMode.byWordWrapping
noDataLabel.numberOfLines = 2
noDataLabel.textAlignment = .center
tableView.backgroundView = noDataLabel
tableView.separatorStyle = .none
}
}
return 0
}
This is how i fetch data:
func attemptFetch() {
let fetchRequest: NSFetchRequest<SwimmingPool> = SwimmingPool.fetchRequest()
let dataSort = NSSortDescriptor(key: "swimminPoolCreatedAt", ascending: false)
fetchRequest.sortDescriptors = [dataSort]
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.debugDescription)")
}
} //End AttemptFetch
here's how i manage the different cases:
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! ParamCell
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
}
}
My problem is that the table is displaying just one result randomly(I have inserted 3 parameters)
Any idea?

Related

CoreData with tableView Sections

I have CoreData Model:
And I try use fetchResultsController to display sections and rows. Sections should be from Category entity and rows should be from Items entity. How I can achieve this?
I use code:
override func numberOfSections(in tableView: UITableView) -> Int {
return fetchResultsController?.sections?.count ?? 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let sectionInfo = fetchResultsController.sections![section]
return sectionInfo.numberOfObjects
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ItemsTableViewCell
if let person = fetchResultsController?.object(at: indexPath) {
cell.textLabel?.text = person.name
}
return cell
}
But with this code I get only Category names. But I want to show Items related to special category. What I should change in
if let person = fetchResultsController?.object(at: indexPath) {
cell.textLabel?.text = person.name
}
to display Items for special Category?
My code for configuring fetchResultsController:
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: guard let indexPath = newIndexPath else { break }
tableView.insertRows(at: [indexPath], with: .fade)
case .delete: guard let indexPath = indexPath else { break }
tableView.deleteRows(at: [indexPath], with: .fade)
case .update: guard let indexPath = indexPath else { break }
tableView.reloadRows(at: [indexPath], with: .fade)
default:
tableView.reloadData()
}
categories = controller.fetchedObjects as! [Category]
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
}
And:
var fetchResultsController: NSFetchedResultsController<Category>!
let request: NSFetchRequest<Category> = Category.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
fetchResultsController = NSFetchedResultsController<Category>(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
try? fetchResultsController?.performFetch()
fetchResultsController?.delegate = self
tableView.reloadData()
You need to base the frc on the Item entity, and use the sectionNameKeyPath to reference the Category name:
var fetchResultsController: NSFetchedResultsController<Item>!
let request: NSFetchRequest<Item> = Item.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(key: "list.name", ascending: true)]
fetchResultsController = NSFetchedResultsController<Item>(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: “list.name”, cacheName: nil)
try? fetchResultsController?.performFetch()
fetchResultsController?.delegate = self
tableView.reloadData()

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

My CoreData entry in TableView reappears when I reload the app after deletion

So I would like to delete entries in my tableview. I am currently making use of coredata. What happens is when I delete my entry in my app, it appears to be deleted but when I reload the application, the entry reappears. It is most probably because I am not deleting the entry in coredata itself, only deleting it in the tableview if that makes any sense.
I have followed multiple guides on how to implement a tableview controller to handle changes including deletion. That is why I am confused as to why my code does not work.
My ViewController:
import UIKit
import CoreData
class ViewController: UITableViewController, NSFetchedResultsControllerDelegate {
let cellId = "cellId"
fileprivate lazy var fetchedResultsController: NSFetchedResultsController<AlarmItem> = {
//create fetch request
let fetchRequest: NSFetchRequest<AlarmItem> = AlarmItem.fetchRequest()
//configure fetch request
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "alarmAttributes", ascending: true)]
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
let managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
fetchedResultsController.delegate = self
return fetchedResultsController
}()
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.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 {
tableView.insertRows(at: [indexPath], with: .fade)
}
break;
case .delete:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .fade)
}
break;
case .update:
//this is mostlikely where the problem lies
//self.tableView.reloadData()
if let indexPath = indexPath, let cell = tableView.cellForRow(at: indexPath) {
configureCell(cell, at: indexPath)
}
break;
case .move:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .fade)
}
if let newIndexPath = newIndexPath {
tableView.insertRows(at: [newIndexPath], with: .fade)
}
break;
#unknown default:
print("Something odd is happening")
}
}
override func viewDidLoad() {
super.viewDidLoad()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
do {
try fetchedResultsController.performFetch()
} catch let err as NSError {
print("Failed to fetch items", err)
}
}
#objc func addAlarmItem(_ sender: AnyObject) {
//print("this works")
let alertController = UIAlertController(title: "Add New Item", message: "Please fill in the blanks", preferredStyle: .alert)
let saveAction = UIAlertAction(title: "Save", style: .default) { [unowned self] action in
//combined string of attributes
let myStrings: [String] = alertController.textFields!.compactMap { $0.text }
let myText = myStrings.joined(separator: ", ")
self.save(myText)
self.tableView.reloadData()
}
let cancelAction = UIAlertAction(title: "Cancel", style: .destructive, handler: nil)
alertController.addTextField { (textField) in
textField.placeholder = "Enter Name of Engineer"
}
alertController.addTextField { (textField) in
textField.placeholder = "Enter Date of Alarm in DD/MM/YYYY"
}
alertController.addTextField { (textField) in
textField.placeholder = "Enter Time of Alarm in 24h (eg: 2300)"
}
alertController.addTextField { (textField) in
textField.placeholder = "Please indicate True/False (type True or False)"
}
alertController.addTextField { (textField) in
textField.placeholder = "Insert comments (if any), or NIL"
}
//work on fullscreen display
//this does not work
//alertController.modalPresentationStyle = UIModalPresentationStyle.overFullScreen
alertController.addAction(saveAction)
alertController.addAction(cancelAction)
present(alertController, animated: true, completion: nil)
}
func save(_ itemName: String) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedContext = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "AlarmItem", in: managedContext)!
let item = NSManagedObject(entity: entity, insertInto: managedContext)
item.setValue(itemName, forKey: "alarmAttributes")
do {
try managedContext.save()
tableView.reloadData()
} catch let err as NSError {
print("Failed to save an item", err)
}
}
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: cellId, for: indexPath)
let alarmItem = fetchedResultsController.object(at: indexPath) as NSManagedObject
cell.textLabel?.text = alarmItem.value(forKeyPath: "alarmAttributes") as? String
return cell
}
func tableView(_ tableView: UITableView!, canEditRowAtIndexPath indexPath: NSIndexPath!) -> Bool {
return true
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
guard editingStyle == .delete else { return }
//fetch
let toBeDeleted = fetchedResultsController.object(at: indexPath)
//delete
fetchedResultsController.managedObjectContext.delete(toBeDeleted)
}
func tableView(_tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "AlarmItem", for: indexPath)
configureCell(cell, at: indexPath)
return cell
}
func configureCell(_ cell: UITableViewCell, at indexPath: IndexPath) {
let alarmItem = fetchedResultsController.object(at: indexPath)
//configure cell
cell.textLabel?.text = alarmItem.value(forKeyPath: "alarmAttributes") as? String
}
}
Please add:
fetchedResultsController.managedObjectContext.save()
as last line in function :
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath)

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
}

tabelview RowInSection fatal error swift

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>?

Resources