Я не понимаю, как реализовать NSFetchedResultsController, поскольку продолжаю получать ошибки. Я пытаюсь подключить Core Data к UITableView, и я не перехожу к следующему шагу, когда UITableView выводит правильное количество строк в соответствии с количеством записей из Core Data. Я получаю это сообщение об ошибке:

*** Terminating app
due to uncaught exception 'NSRangeException', reason: '*** 
[__NSSingleObjectArrayI objectAtIndex:]: index 1 beyond bounds [0 .. 0]'

Вот мой код ниже:

import UIKit
import CoreData

class CustomerProfileViewController: UITableViewController, NSFetchedResultsControllerDelegate {

    var coreDataStack: CoreDataStack!

    var itemList: [NSManagedObject] = [] // I hope to have the fetch results controller manage this object

    // The data model contains one entity "Item" with one attribute "name"

    lazy var fetchResultsControllerItem: NSFetchedResultsController<Item> = {

        // Initialize Fetch Request
        let fetchRequest: NSFetchRequest<Item> = Item.fetchRequest()

        // Configure Fetch Request
        fetchRequest.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]

        // Initialize Fetch Results Controller
        let fetchResultsControllerItem = 
            NSFetchedResultsController(fetchRequest: fetchRequest,
            managedObjectContext: self.coreDataStack.managedContext,
            sectionNameKeyPath: nil,
            cacheName: nil)
        fetchResultsControllerItem.delegate = self
        return fetchResultsControllerItem
    }()

    override func viewDidLoad() {

        super.viewDidLoad()

        do {
            try fetchResultsControllerItem.performFetch()
            print("Fetch worked! 🐺")
        } catch let error as NSError {
            print("Could not save. \(error), \(error.userInfo)")
        }

        let cellNib = UINib(nibName: "SummaryCell", bundle: nil)
        tableView.register(cellNib, forCellReuseIdentifier: "SummaryCell")
        let cellNib = UINib(nibName: "ListingsCell", bundle: nil)
        tableView.register(cellNib, forCellReuseIdentifier: "ListingsCell")
    }

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 2 // Corresponds to the custom table view cells as Nib files
    }

    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        if section == 0 {
            return "SUMMARY"
        } else if section == 1 {
            return "ITEM LISTINGS"
        } else {
            return ""
        }
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if section == 0 {
            return 1
        } else if section == 1 {

/* Adding the "guard let sections" to the "return
sectionInfo.numberOfObject" below causes an error: *** Terminating app
due to uncaught exception 'NSRangeException', reason: '*** 
[__NSSingleObjectArrayI objectAtIndex:]: index 1 beyond bounds [0 .. 0]' */
enter code here
            guard let sections = fetchResultsControllerItem.sections else {
                return 0
            }
            let sectionInfo = sections[section]
            return sectionInfo.numberOfObjects

        } else {
            return 1
        }
    }

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

        if indexPath.section == 0 {
            let cell = tableView.dequeueReusableCell(withIdentifier: "SummaryCell", for: indexPath) as! SummaryCell
            cell.totalNumberOfItems?.text = "\(itemList.count)"
            return cell
        } else if indexPath.section == 1 {

            let cell = tableView.dequeueReusableCell(withIdentifier: "ListingsCell", for: indexPath) as! ListingsCell
            let listItem = fetchResultsControllerItem.object(at: indexPath)
            cell.itemName?.text = listItem.name
            return cell
        }
        return UITableViewCell()
    }
}

Ниже показан файл, содержащий реализацию CoreDataStack:

import Foundation
import CoreData

class CoreDataStack {

    private let modelName: String

    init(modelName: String) {
        self.modelName = modelName
    }

    lazy var managedContext: NSManagedObjectContext = {
        return self.storeContainer.viewContext
    }()

    private lazy var storeContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: self.modelName)
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                print("Unresolved error \(error), \(error.userInfo)")
            }
        })
        return container
    }()

    func saveContext() {
        guard managedContext.hasChanges else { return }

        do {
            try managedContext.save()
        } catch let error as NSError {
            print("Unresolved error \(error), \(error.userInfo)")
        }
    }
}

Я использовал Liya, чтобы подтвердить, что есть элементы с именами в записи, после изучения файла sqlite. Всем, у кого есть какие-либо советы или идеи, я буду признателен за любую помощь. Спасибо.

1
William Chiang 26 Фев 2018 в 05:31

1 ответ

Лучший ответ

Проблема в том, что в представлении FetchedResultsController, если мир, есть только один раздел: раздел 0. Но ваш tableView имеет два раздела, и вы хотите, чтобы FRC предоставлял данные для раздела 1. Поэтому вам нужно переназначить FRC indexPaths в нужный раздел в табличном представлении в методах источника данных tableView. Итак, в numberOfRowsInSection ваш код в настоящее время запрашивает у FRC количество объектов в разделе 1, и FRC выдает ошибку, поскольку, насколько это возможно, раздела 1 нет. Просто замените:

        let sectionInfo = sections[section]
        return sectionInfo.numberOfObjects

С участием:

        let sectionInfo = sections[0]
        return sectionInfo.numberOfObjects

В этом методе. Затем в cellForRowAt замените это:

        let cell = tableView.dequeueReusableCell(withIdentifier: "ListingsCell", for: indexPath) as! ListingsCell
        let listItem = fetchResultsControllerItem.object(at: indexPath)
        cell.itemName?.text = listItem.name
        return cell

С этим:

        let cell = tableView.dequeueReusableCell(withIdentifier: "ListingsCell", for: indexPath) as! ListingsCell
        let listItem = fetchResultsControllerItem.fetchedObjects![indexPath.row]
        cell.itemName?.text = listItem.name
        return cell

(Здесь используется свойство fetchedObjects FRC, чтобы избежать использования неправильного раздела в indexPath в object(at:)).

0
pbasdf 26 Фев 2018 в 12:22