无法保存到核心数据中
Posted
技术标签:
【中文标题】无法保存到核心数据中【英文标题】: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
变量?
我发布了我的其余代码。
这个查询解决了吗?
是的,已经解决了。
【参考方案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 堆栈,然后尝试在其上保存上下文。
尝试以下方法:
声明你的 NSPersistentContainer 引用。
var container: NSPersistentContainer? = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer
使用容器的后台任务队列来保存上下文。
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。
我假设样板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 有一些选项可以利用这种机会进行错误处理。因此,我不喜欢强制展开。尽管如此,您的方法也可以正常工作。以上是关于无法保存到核心数据中的主要内容,如果未能解决你的问题,请参考以下文章