【问题标题】:FetchResultController not able to fetch dataFetchResult 控制器无法获取数据
【发布时间】:2025-12-21 13:10:11
【问题描述】:

我是 ios 应用程序开发的新手。我尝试制作一个包含 tableView 的简单应用程序,我们可以向 tableView 添加行,它可以使用 CoreData 将该数据保存在数据库中。我正在使用 NSFetchedResultController 来实现这一点,但是当我尝试在模拟器中运行它时,我添加到数据库中的数据无法获取。我尝试了其他各种解决方案,但无法解决。

CoreDataStack.swift

import Foundation
import CoreData

class CoreDataStack {
    private init() {}
    static let shared = CoreDataStack()
    lazy var context = CoreDataStack.shared.persistentContainer.viewContext
    var fetchedResultsController: NSFetchedResultsController<UserEntity> {
        let fetchRequest: NSFetchRequest<UserEntity> = UserEntity.fetchRequest()
        let nameSortDescriptor = NSSortDescriptor(key: "name", ascending: true)
        let passwordSortDescriptor = NSSortDescriptor(key: "password", ascending: true)
        fetchRequest.sortDescriptors = [nameSortDescriptor,passwordSortDescriptor]
        let fetchResultsController = NSFetchedResultsController<UserEntity>(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
        return fetchResultsController
    }
    lazy var persistentContainer: NSPersistentContainer = {

        let container = NSPersistentContainer(name: "NSFRCCoreData")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {

                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        return container
    }()

    // MARK: - Core Data Saving support

    func saveContext () {
        if context.hasChanges {
            do {
                try context.save()
            } catch {

                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        }
    }

}

和我的视图控制器

import UIKit
import CoreData
class ViewController: UIViewController {
 let fetchResultController = CoreDataStack.shared.fetchedResultsController
    @IBOutlet weak var tableView: UITableView!
    override func viewDidLoad() {
        super.viewDidLoad()
        print(NSTemporaryDirectory())
        title = "List"
        tableView.dataSource = self

    }
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        CoreDataStack.shared.fetchedResultsController.delegate = self
        try! CoreDataStack.shared.fetchedResultsController.performFetch()

    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func addItems(_ sender: UIBarButtonItem) {
        let alert =  UIAlertController(title: "Enter The Item Name", message: nil, preferredStyle: .alert)
        alert.addTextField(configurationHandler: nil)
        alert .addTextField { (passwordTextField) in
            let placeholder = NSAttributedString(string: "Password", attributes: nil)
            passwordTextField.attributedPlaceholder = placeholder
        }
        let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
        let save = UIAlertAction(title: "Save", style:.default) { [unowned self] (action) in
            guard let nameTextfield = alert.textFields?.first, let name = nameTextfield.text else {
                return
            }
            guard let passwordTextfield = alert.textFields?[1], let password = passwordTextfield.text else {
                return
            }
            let entity = NSEntityDescription.entity(forEntityName: "UserEntity", in: CoreDataStack.shared.context)
            let userEntity = UserEntity(entity: entity!, insertInto: CoreDataStack.shared.context)
            userEntity.name = name
            userEntity.password = password
            try? CoreDataStack.shared.context.save()

            self.tableView.reloadData()
        }
        alert.addAction(cancel)
        alert.addAction(save)
        self.present(alert, animated: true, completion: nil)
    }


}

extension ViewController: UITableViewDataSource {
    func numberOfSections(in tableView: UITableView) -> Int {
        guard let sectionCount = CoreDataStack.shared.fetchedResultsController.sections?.count else {
            return 0
        }
        return sectionCount
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        guard let sections = CoreDataStack.shared.fetchedResultsController.sections else {
            return 0
        }

        return sections[section].numberOfObjects
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "DataCell", for: indexPath)
         let userEntity = CoreDataStack.shared.fetchedResultsController.object(at: indexPath)
        cell.textLabel?.text = userEntity.name
        cell.detailTextLabel?.text = userEntity.password
        return cell

    }
}
extension ViewController: NSFetchedResultsControllerDelegate {
    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)
            }

        case .delete:
            if let indexPath = indexPath {
                tableView.deleteRows(at: [indexPath], with: .fade)
            }
        case .move:
            if let indexPath = indexPath, let newIndexPath = newIndexPath {
                tableView.moveRow(at: indexPath, to: newIndexPath)
            }
        case .update:
            if let indexPath = indexPath, let cell = tableView.cellForRow(at: indexPath){
                cell.textLabel?.text = "Hello"
            }
        }
    }
    func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        tableView.endUpdates()
    }
}

TableView 不显示任何数据,但是当我使用 sqlite 浏览器检查时,它包含保存数据。

【问题讨论】:

  • 您的 fetchedResultsController 是一个计算属性,每次计算时都会返回一个不同的 FRC 实例。因此,performFetch 在 tableView 数据源方法中与 FRC 不同的实例上执行。
  • @pdasdf 感谢它现在运行良好
  • @pbasdf 如果你有答案,你真的应该把它作为答案而不是评论。这样做会使问题看起来仍然没有答案。
  • @TomHarrington 你是对的,对不起。我现在将添加一个答案。

标签: ios core-data


【解决方案1】:

您的 fetchedResultsController 是一个计算属性,每次计算时都会返回一个不同的 FRC 实例。因此,performFetch 在 tableView 数据源方法中与 FRC 不同的实例上执行。

【讨论】: