核心数据与 NSFetchedResultsController:更改后更新数据

Core Data with NSFetchedResultsController: Updating Data After Changes

过去 18 个小时左右,我一直在研究 Core Data。我正在使用 NSFetchedResultsController 获取数据并使用 UITableView 显示数据。添加新记录和删除所选记录不是我的问题。

class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    // MARK: - Instance variables
    private let persistentContainer = NSPersistentContainer(name: "Profiles") // core data model file (.xcdatamodeld)
    var managedObjectContext: NSManagedObjectContext?

    // MARK: - IBOutlets
    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // loading persistentContainer //
        persistentContainer.loadPersistentStores { (persistentStoreDescription, error) in
            if let error = error {
                print("Unable to Load Persistent Store")
            } else {
                do {
                    try self.fetchedResultsController.performFetch()
                } catch {
                    let fetchError = error as NSError
                    print("\(fetchError), \(fetchError.localizedDescription)")
                }
            }
        }

        // notifications //
        NotificationCenter.default.addObserver(self, selector: #selector(profileDidUpdate), name: NSNotification.Name(rawValue: "HomeViewControllerPictureDidSelect"), object: nil)
    }

    // MARK: - fetchedResultsController(controller with the entity)
    fileprivate lazy var fetchedResultsController: NSFetchedResultsController<Person> = {
        // Create Fetch Request with Entity
        let fetchRequest: NSFetchRequest<Person> = Person.fetchRequest()

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

        // Create Fetched Results Controller
        let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil)

        // Configure Fetched Results Controller
        fetchedResultsController.delegate = self
        return fetchedResultsController
    }()
    // MARK: - fetchedResultsController


    // MARK: - Notifications
    @objc func profileDidUpdate(notification: NSNotification) {
        let profile = notification.object as! Profile
        let context = persistentContainer.viewContext
        let entity = NSEntityDescription.entity(forEntityName: "Person", in: context)
        let newPerson = NSManagedObject(entity: entity!, insertInto: context)
        newPerson.setValue(profile.uuid, forKey: "uuid") // uuid is used to make each record unique
        newPerson.setValue(profile.firstName, forKey: "firstName")
        newPerson.setValue(profile.lastName, forKey: "lastName")
        newPerson.setValue(profile.age, forKey: "age")
        newPerson.setValue(profile.pictData, forKey: "pictData")
        do {
            try context.save()
            print("saved...")
        } catch {
            print("failed saving")
        }
    }
    // MARK: - Notifications
}

extension HomeViewController: NSFetchedResultsControllerDelegate {
    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;
        default:
            print("...")
        }
    }

    func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
    }
}

如上所示,我从另一个视图控制器创建了一条新记录,它将模型(Profile)的对象发送到当前视图控制器(HomeViewController)。由于 NSFetchedResultsController.

,我不必重新加载 table

该实体有几个属性(年龄、名字、姓氏、pictData、uuid)。我想用两个属性更改列表中的选定记录:firstName 和 lastName。 uuid 属性用于标识特定记录。

class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    @IBAction func editTapped(_ sender: UIButton) {
        guard let indexPath = tableView.indexPathForSelectedRow else {
            return
        }
        let selectedRow = indexPath.row
        if selectedRow >= 0 {
            editRecord(index: selectedRow)
        }
    }

    func editRecord(index: Int) {
        let indexPath = IndexPath(row: index, section: 0)
        let person = fetchedResultsController.object(at: indexPath)
        let uuid = person.uuid!
        let context = self.persistentContainer.viewContext
        let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Person")
        fetchRequest.predicate = NSPredicate(format: "uuid == %@", uuid)
        do {
            let result = try context.fetch(fetchRequest)
            if (result.count > 0) {
                let managedObject = result[0] as! NSManagedObject
                managedObject.setValue("Donald", forKey: "firstName")
                managedObject.setValue("Washington", forKey: "lastName")
                try context.save()
                print("Changes saved...")
            }
        } catch {
            print("Failed")
        }
    }
}

现在,如果我点击编辑按钮,应用程序不会立即更新列表。如果我重新启动应用程序,我会看到更改。那么,当我对所选记录进行更改时,如何将 table 更新为 NSFetchedResultsController 呢?谢谢。

由于您使用的是 NSFetchedResultsControllerDelegate,您需要处理(针对您的特定用例)didChange 方法中 NSFetchedResultsChangeType 的以下情况:

  • 插入
  • 删除
  • 更新

您的函数应如下所示:

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:
      tableView.reloadRows(at: [indexPath], with: .automatic)
      break;
  default:
      print("...")
  }
}