【问题标题】:Unable to save into core data无法保存到核心数据中
【发布时间】:2018-06-15 04:23:45
【问题描述】:

我有 2 个 ViewController 连接到选项卡栏控制器。一个 ViewController 应该显示来自核心数据的表格数据,另一个有两个输入文本字段和输入按钮以将输入数据保存到核心数据。但是,我无法使用“var managedObjectContext:NSManagedObjectContext?”进行保存。我没有使用任何 segues 来传递数据。我编码正确吗?

import UIKit
import CoreData

class AddNameViewController: UIViewController {

// MARK: - Properties

@IBOutlet var firstNameTextField: UITextField!
@IBOutlet var lastNameTextField: UITextField!

var person: Person?
var managedObjectContext: NSManagedObjectContext?


override func viewDidLoad() {
    super.viewDidLoad()

    title = "Add Name"

}

// MARK: - Actions

@IBAction func save(_ sender: UIButton) {
    guard let managedObjectContext = managedObjectContext else { return }

    if person == nil {
        let newName = Person(context: managedObjectContext)

        newName.firstName = firstNameTextField.text
        newName.lastName = lastNameTextField.text
        newName.createdAt = Date().timeIntervalSince1970

    }
    try? managedObjectContext.save()
}

}

我可能会误导大家。这是我的表格视图代码。而且我在 AppDelegate 中没有样板。

import UIKit
import CoreData

class ViewController: UIViewController {

@IBOutlet var messageLabel: UILabel!
@IBOutlet var tableView: UITableView!
@IBOutlet var activityIndicatorView: UIActivityIndicatorView!

// MARK: - Persistent Container

private let persistentContainer = NSPersistentContainer(name: "People")

// MARK: - Fetch results controller

fileprivate lazy var fetchedResultsController:      NSFetchedResultsController<Person> = {
    let fetchRequest: NSFetchRequest<Person> = Person.fetchRequest()

    fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(Person.createdAt), ascending: true
        )]

    let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.persistentContainer.viewContext, sectionNameKeyPath: #keyPath(Person.lastName), cacheName: nil)

    fetchedResultsController.delegate = self

    return fetchedResultsController
}()

override func viewDidLoad() {
    super.viewDidLoad()

    title = "People"

    persistentContainer.loadPersistentStores { (persistentSoreDescription, error) in
        if let error = error {
            print("Unable to load persistent store")
            print("\(error), \(error.localizedDescription)")
        } else {
            self.setupView()

            do {
                try self.fetchedResultsController.performFetch()
            } catch {
                let fetchError = error as NSError
                print("Unable to perform fetch request")
                print("\(fetchError), \(fetchError.localizedDescription)")
            }

            self.updateView()
        }
    }

    NotificationCenter.default.addObserver(self, selector: #selector(applicationDidEnterBackground(_:)), name: Notification.Name.UIApplicationDidEnterBackground, object: nil)
}

// MARK: - Views

private func setupView() {
    setupMessageLabel()

    updateView()
}

fileprivate func updateView() {
    var hasNames = false

    if let people = fetchedResultsController.fetchedObjects {
        hasNames = people.count > 0
    }

    tableView.isHidden = !hasNames
    messageLabel.isHidden = hasNames

    activityIndicatorView.stopAnimating()
}

private func setupMessageLabel() {
    messageLabel.text = "You do not have names yet."
}

// MARK: - Notification handling

@objc func applicationDidEnterBackground(_ notification: Notification) {
    do {
        try persistentContainer.viewContext.save()
    } catch {
        print("Unable to save changes")
        print("\(error), \(error.localizedDescription)")
    }
}

}

extension ViewController: NSFetchedResultsControllerDelegate {

func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    tableView.beginUpdates()
}

func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    tableView.endUpdates()

    updateView()
}

    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? PersonTableViewCell {
            configure(cell, at: indexPath)
        }
    default:
        break

    }
}

    func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
    switch type {
    case .insert:
        tableView.insertSections(IndexSet(integer: sectionIndex), with: .fade)
    case .delete:
        tableView.deleteSections(IndexSet(integer: sectionIndex), with: .fade)
    default:
        break
    }
}

}

    extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
    guard let sections = fetchedResultsController.sections  else { return 0 }
    return sections.count
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    guard let sectionInfo = fetchedResultsController.sections?[section] else { fatalError("Unexpected section") }
    return sectionInfo.numberOfObjects
}

func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    guard let sectionInfo = fetchedResultsController.sections?[section] else { fatalError("Unexpected section") }
    return sectionInfo.name
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: PersonTableViewCell.reuseIdentifier, for: indexPath) as? PersonTableViewCell else {
        fatalError("Unexpected indexPath")
    }

    // Configure cell
    configure(cell, at: indexPath)

    return cell
}

func configure(_ cell: PersonTableViewCell, at indexPath: IndexPath) {
    let name = fetchedResultsController.object(at: indexPath)

    // Configure cell
    cell.firstNameLabel.text = name.firstName
    cell.lastNameLabel.text = name.lastName
}

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
    if editingStyle == .delete {
        let name = fetchedResultsController.object(at: indexPath)

        name.managedObjectContext?.delete(name)
    }
}

}

extension ViewController: UITabBarDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    tableView.deselectRow(at: indexPath, animated: true)
}

}

【问题讨论】:

  • 数据保存后没有通知表格,请尝试重新加载表格。
  • 你在哪里设置managedObjectContext变量?
  • 我发布了我的其余代码。
  • 这个查询解决了吗?
  • 是的,已经解决了。

标签: swift core-data


【解决方案1】:

我认为您需要插入要保存的新记录,如下所示

//Create Reference for New AudioFile
let newPerson = NSEntityDescription.insertNewObject(forEntityName: "Person", into: context) as! Person

//Set Values
newPerson.fileName = firstNameTextField.text
newPerson.lastName = lastNameTextField.text
newPerson.craetedAt = Date().timeIntervalSince1970

//Save Context here
self.saveChanges() 


//MARK:- Saves all changes
func saveChanges()
{
  do{
       try context.save()
    }
    catch let error as NSError
    {
       // failure
       print(error)
    }
}

另外我会建议你检查与核心数据相关的工作代码

Github 链接 - https://github.com/RockinGarg/CoreData_Swift_Demo.git

该项目使用单独的核心类(NSObject 类)进行保存、获取、按 ID 删除、删除实体数据和谓词

【讨论】:

    【解决方案2】:

    您的NSPersistentContainer 参考在哪里?您必须首先使用上述容器初始化您的 CoreData 堆栈,然后尝试在其上保存上下文。

    尝试以下方法:

    1. 声明你的 NSPersistentContainer 引用。

      var container: NSPersistentContainer? = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer
      
    2. 使用容器的后台任务队列来保存上下文。

      container?.performBackgroundTask{ _ in
          guard let managedObjectContext = managedObjectContext else { return }
      
          if person == nil {
              let newName = Person(context: managedObjectContext)
      
              newName.firstName = firstNameTextField.text
              ...
      
          }
      
          try? managedObjectContext.save() 
      }
      

    详情请查看docs

    1. 我假设样板persistentContainer 代码是您在AppDelegate 中已经拥有的东西,因为您正在使用CoreData,即,

      lazy var persistentContainer: NSPersistentContainer = {
      
          let container = NSPersistentContainer(name: "myModel")
          container.loadPersistentStores(completionHandler: { (storeDescription, error) in
              if let error = error as NSError? {
                  fatalError("Unresolved error \(error), \(error.userInfo)")
              }
          })
          return container
      }()
      

    【讨论】:

    • 不要将container 声明为可选。该容器必须存在以及AppDelegate。如果AppDelegate 丢失,应用程序甚至不会启动。强制转换 AppDelegate (UIApplication.shared.delegate as! AppDelegate).persistentContainer
    • 我明白你的意思。但是,Apple 并不保证 persistentContainer 属性始终可靠。他们建议使用可选项来引用它。 This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
    • 此外,Swift 有一些选项可以利用这种机会进行错误处理。因此,我不喜欢强制展开。尽管如此,您的方法也可以正常工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多