Как отображать в TableView значения из двух сущностей СoreData?

swift
uitableview
coredata

#1

При выгрузке данных из СoreData в разные UITableViewCell происходите ошибка SIGABART.

Пробовал ставить breakpoint, ошибка вылетает после строки
return sections2![section].numberOfObjects
метода func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

Сам код TableView

import UIKit
import CoreData

class TableView: UIViewController, UITableViewDataSource, UITableViewDelegate, NSFetchedResultsControllerDelegate{
    
    @IBOutlet weak var tableView: UITableView!

    var fetchedResultsController = CoreDataManager.instance.fetchedResultsController("Customer", keyForSort: "name")
    
    var fetchedResultsController2 = CoreDataManager.instance.fetchedResultsController("Seller", keyForSort: "name_seller")
    
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate=self
        tableView.dataSource=self
        
        fetchedResultsController.delegate = self
        do {
            try fetchedResultsController.performFetch()
        } catch {
            print(error)
        }
        
        fetchedResultsController2.delegate = self
        do {
            try fetchedResultsController2.performFetch()
        } catch {
            print(error)
        }
    }
    
    @IBAction func AddCustomer(_ sender: AnyObject) {
        performSegue(withIdentifier: "customersToCustomer", sender: nil)
    }
    
    @IBAction func AddSeller(_ sender: AnyObject) {
        performSegue(withIdentifier: "sellersToSeller", sender: nil)
    }
    
    // MARK: - Table View Data Source
    
     func numberOfSections(in tableView: UITableView) -> Int {
        return 2
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if section == 0 {
            let sections = fetchedResultsController.sections
            return sections![section].numberOfObjects
        }
        else if section == 1  {
            let sections2 = fetchedResultsController2.sections
            return sections2![section].numberOfObjects
        }
        else {
            return 1
        }
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        

        if indexPath.section == 0 {
            let cell = tableView.dequeueReusableCell(withIdentifier: "0", for: indexPath) as! TableViewCell
            let customer = fetchedResultsController.object(at: indexPath) as! Customer
            cell.textLabel?.text = customer.name
            return (cell)
            
        } else  {
            let cell2 = tableView.dequeueReusableCell(withIdentifier: "1", for: indexPath) as! TableViewCell2

            let seller = fetchedResultsController2.object(at: indexPath) as! Seller
            cell2.textLabel?.text = seller.name_seller
            return (cell2)
        }
    }

    // MARK: - Table View Delegate

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            let managedObject = fetchedResultsController.object(at: indexPath) as! NSManagedObject
            CoreDataManager.instance.managedObjectContext.delete(managedObject)
            CoreDataManager.instance.saveContext()
        }
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let customer = fetchedResultsController.object(at: indexPath) as? Customer
        performSegue(withIdentifier: "customersToCustomer", sender: customer)
        
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "customersToCustomer" {
            let controller = segue.destination as! CustomerViewController
                        controller.customer = sender as? Customer
        }
    }

    // MARK: - Fetched Results Controller Delegate
    
    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: .automatic)
            }
            
        case .update:
            if let indexPath = indexPath {
                if indexPath.section == 0{
                    let customer = fetchedResultsController.object(at: indexPath) as! Customer
                    let cell = tableView.cellForRow(at: indexPath)
                    cell!.textLabel?.text = customer.name
                }
                else{
                    let seller = fetchedResultsController2.object(at: indexPath) as! Seller
                    let cell2 = tableView.cellForRow(at: indexPath)
                    cell2!.textLabel?.text = seller.name_seller
                }
            }
  
        case .move:
            if let indexPath = indexPath {
                tableView.deleteRows(at: [indexPath], with: .automatic)
            }
            if let newIndexPath = newIndexPath {
                tableView.insertRows(at: [newIndexPath], with: .automatic)
            }
            
        case .delete:
            if let indexPath = indexPath {
                tableView.deleteRows(at: [indexPath], with: .automatic)
            }
        }
    }
    
    func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        tableView.endUpdates()
    }
}

#2

У каждого fetchedResultsController одна секция, они же не связаны друг с другом, в методе делегата didChange вам нужно определять секцию исходя из контроллера, что то вроде этого:

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
    let section = controller == fetchedResultsController ? 0 : 1
    switch type {
    case .insert:
        tableView.insertRows(at: [[section, newIndexPath!.row]], with: .automatic)
    case .update:
        if section == 0 {
            let customer = controller.object(at: indexPath!) as! Customer
            let cell = tableView.cellForRow(at: [0, indexPath!.row])
            cell!.textLabel?.text = customer.name
        } else {
            let seller = controller.object(at: indexPath!) as! Seller
            let cell2 = tableView.cellForRow(at: [1, indexPath!.row])
            cell2!.textLabel?.text = seller.name_seller
        }
    case .delete:
        tableView.deleteRows(at: [[section, indexPath.row]], with: .automatic)
    default: break
    }
}

#3

Изменил по вашему примеру fetchedResultsController, но все равно та же ошибка.

Ошибка по брекпоинтам опять тут:

let sections2 = fetchedResultsController2.sections
return sections2![section].numberOfObjects // <----

Пробовал заменить это выражение на

return fetchedResultsController.fetchedObjects!.count
, но не помогло


#4

Поменял

 func numberOfSections(in tableView: UITableView) -> Int {
      return 1    // тут было return 2
    }

И оно запустилось, но значения добавляются только из одной сущности.
Если добавляю для другой, то в логе пишется:

2017-12-22 15:11:02.956 tableViewWithDifferentEntities[57913:3691569] *** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3600.7.47/UITableView.m:1523
2017-12-22 15:11:02.973294+0200 tableViewWithDifferentEntities[57913:3691569] [error] error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. attempt to insert row 1 into section 1, but there are only 1 sections after the update with userInfo (null)
CoreData: error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. attempt to insert row 1 into section 1, but there are only 1 sections after the update with userInfo (null)
SAVE


#5
func numberOfSections(in tableView: UITableView) -> Int {
    return 2
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if section == 0 {
        let sections = fetchedResultsController.sections
        return sections![0].numberOfObjects
    } else {
        let sections = fetchedResultsController2.sections
        return sections![0].numberOfObjects
    }
}

Попробуйте не просто бездумно копировать, а включить голову :slight_smile:


#6

Вы мне уже со всеми методами помогли, но ошибка все равно осталась.

CoreData: error: NSFetchedResultsController: no section at index 1 in sections list
2017-12-23 14:02:58.119 tableViewWithDifferentEntities[62462:4055905] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘no section at index 1’

Как оно пишет, что нет раздела под индексом 1 , если я передаю 2йку в numberOfSections?


#7

У вас нет секции под индексом 1, ищите где вы обращаетесь к fetchedResultsController по indexPath с индексом секции 1, например здесь:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    if indexPath.section == 0 {
        let cell = tableView.dequeueReusableCell(withIdentifier: "0", for: indexPath) as! TableViewCell
        let customer = fetchedResultsController.object(at: indexPath) as! Customer
        cell.textLabel?.text = customer.name
        return (cell)
    } else {
        let cell2 = tableView.dequeueReusableCell(withIdentifier: "1", for: indexPath) as! TableViewCell2
        let seller = fetchedResultsController2.object(at: [0, indexPath.row]) as! Seller
        cell2.textLabel?.text = seller.name_seller
        return (cell2)
    }
}

#8

Хочу переделать программу, чтобы в ней можно было отображать значения из нескольких сущностей. Мне необходимо переделать условие:
let section = controller == fetchedResultsController ? 0 : 1

Я понял это условие так :" fetchedResultsController может быть , как 0, так и 1"
По разному пробывал, но никакое из них некорректно
let section = controller == fetchedResultsController ? 0 : 1 : 2 : 3
let section = controller == fetchedResultsController ? 0 : 1...3
let section = controller == fetchedResultsController ? 0 : (1,2,3)// пробовал передать котреж

Можете, пожалуйста, подсказать как изменить условие так,
чтобы fetchedResultsController мог быть, допустим, 5 или 7 ?