Swift — 4 —核心数据—第2部分创建一个简单的应用程序

如果您想进行完整的iOS培训,可以通过以下详细信息与我联系,我会使用目标C或Swift提供实时iOS应用培训

skype:alok.upadhyay32

邮件:meiosdose@gmail.com

应用程式:+ 91–7838552946

第1部分。

大家好。

在本教程中,我们将使用核心数据和Swift 4创建一个应用程序。我们将能够在核心数据中插入更新删除获取个人记录。 Person将具有两个属性name和ssn(社会保险号)。 我们还将学习如何使用谓词执行条件操作(filter或sqlite中的where子句)。 这是源代码的链接。

请输入☕和开始代码–

让我们创建新项目,如附加的屏幕截图:

给它起一个名字“ PersonData”,选择语言“ Swift”并选中“ Use Core Data”和“ Include Unit Tests(是的,我们还将进行单元测试:))”:

单击下一步。

现在查看您的项目结构。 它有一个名为“ PersonData.xcdatamodeld”的文件。

那么“ PersonData.xcdatamodeld”是什么?

在上述步骤中,当我们在Xcode中启动新项目并打开模板选择对话框时,选中“使用核心数据”复选框,将创建核心数据模型的源文件作为模板的一部分。 该源文件将具有扩展名.xcdatamodeld这是一个xml文件 。 在导航器区域中选择该文件以显示Core Data模型编辑器。 单击“显示包内容”,也可以打开和查看文件。

双击内容,它不过是一个xml文件:

您可以使用“ xcdatamodeled”的可视化编辑器来创建“实体”,“实体属性”,“获取请求”,“配置”和“关系”。 当我们执行核心数据迁移时,此文件非常重要。

现在,让我们深入研究“ AppDelegate.swift”文件并讨论代码。 AppDelegate文件包含与核心数据相关的应用程序生命周期方法和代码存根。 我们仅对核心数据感兴趣。 它包含了:

  1. 导入CoreData框架
  2. 初始化NSPersistentContainer类,然后初始化核心数据堆栈对象(托管对象模型,PersistentStoreCoordinator,托管对象上下文)。
  3. 一个名为saveContext()的方法。 它将托管对象模型保存到存储中。
  4. 当应用程序即将终止时,名为applicationWillTerminate的应用程序生命周期方法也会调用saveContext()来将数据保存在存储中。

下面的代码创建一个NSPersistentContainer对象。 NSPersistentContainer的实例包括表示运行中的核心数据堆栈所需的所有对象。

  // MARK:-核心数据栈 

懒惰的varpersistentContainer:NSPersistentContainer = {

让容器= NSPersistentContainer(名称:“ PersonData”)

container.loadPersistentStores(completionHandler:{(storeDescription,error)在

如果让error = error as NSError? {
fatalError(“未解决的错误\(错误),\(error.userInfo)”)
}
})
返回容器
}()

NSPersistentContainer

一个在您的应用程序中封装核心数据堆栈(核心数据的心脏)的容器。 NSPersistentContainer通过处理以下对象的创建,简化了Core Data堆栈的创建/初始化和管理:

  • 托管对象模型(NSManagedObjectModel),
  • 持久性存储协调器(NSPersistentStoreCoordinator),
  • 和托管对象上下文(NSManagedObjectContext)。

下面的代码

 让容器= NSPersistentContainer(名称:“ PersonData”) 

用给定名称(PersonData) 初始化一个持久性容器。

参数—名称 — NSPersistentContainer对象的名称。

返回值 —用给定名称初始化的持久性容器。

持久容器初始化后,我们需要执行

  container.loadPersistentStores(completionHandler:{(storeDescription,error)在 

如果让error = error as NSError? {
fatalError(“未解决的错误\(错误),\(error.userInfo)”)
}
})
返回容器

指示容器加载持久性存储并完成Core Data堆栈的创建。

一旦完成处理程序启动,堆栈将完全初始化并可以使用。

storeDescription

就像该类的名称所暗示的那样,NSPersistentStoreDescription类封装了信息和配置,以将持久性存储添加到持久性存储协调器。 换句话说,它描述了一个持久性存储。

错误

如果持久性存储的加载出错,则将填充NSError值。

致命错误()

致命错误将导致应用程序生成崩溃日志并终止。 尽管此功能在开发过程中可能很有用,但您不应在运输应用程序中使用此功能。

检查错误消息以确定实际的问题是什么。 出现错误的典型原因包括:

  • 父目录不存在,无法创建或不允许写入。
  • 由于锁定设备时的权限或数据保护,无法访问持久性存储。
  • 设备空间不足。
  • 无法将存储库迁移到当前模型版本-非常重要的一个,此错误将为您提供有关核心数据迁移过程失败的线索,开发人员可以在其核心数据迁移失败的调试过程中获得帮助。

为什么我们在这里使用lazy var?

这是持久性容器类的延迟初始化延迟初始化是延迟对象的创建,值的计算或其他一些昂贵的任务(如在对象下 初始化 )的策略

  • 托管对象模型(NSManagedObjectModel),
  • 持久性存储协调器(NSPersistentStoreCoordinator),
  • 和托管对象上下文(NSManagedObjectContext)。

直到第一次需要持久性容器

我必须说要了解NSPersistentContainer类,请查看NSPersistentContainer的文档。 双击您的xcode项目中的NSPersistentContainer类,然后单击跳转到定义。 您将了解为什么它是核心数据功能的支柱。

让我们看看AppDelegate文件中还有什么。

saveContext()

  // MARK:-核心数据保存支持 
  func saveContext(){ 
让上下文= persistentContainer.viewContext
如果context.hasChanges {
做{
尝试context.save()
} {
//用代码替换此实现,以正确处理错误。
// fatalError()使应用程序生成崩溃日志并终止。 尽管此功能在开发过程中可能很有用,但您不应在运输应用程序中使用此功能。
让nserror =错误为NSError
fatalError(“未解决的错误\(nserror),\(nserror.userInfo)”)
}
}
}

在下面的代码中,我们调用持久性容器的视图上下文对象。 此视图上下文是ManagedObjectContext类类型的属性的名称。

 让上下文= persistentContainer.viewContext 

默认情况下,视图上下文是主要上下文。 默认的并发类型是MainQueueConcurrencyType。 在Core Data中,托管对象上下文可以与两种并发模式一起使用,这两种并发模式由NSMainQueueConcurrencyTypeNSPrivateQueueConcurrencyType定义。

NSMainQueueConcurrencyType特别用于您的应用程序界面,并且只能在应用程序的主队列上使用。

NSPrivateQueueConcurrencyType配置为后台任务创建自己的队列。

关于并发性有很多讨论,可能需要一些时间:)。

context.hasChanges

hasChanges方法查找对托管对象模型所做的更改。 如果有任何更改,我们调用上下文对象的save()方法以最终保存我们的数据。 save()方法会引发错误,因此我们必须处理这些错误。 当我们为插入,更新和删除编写一些代码时,对其进行讨论会更有意义。

AppDelegate文件中最后剩下的东西是:

applicationWillTerminate

  func applicationWillTerminate(_ application:UIApplication){ 
//在应用程序即将终止时调用。 如果合适,保存数据。 另请参阅applicationDidEnterBackground:。
//在应用程序终止之前将更改保存在应用程序的托管对象上下文中。
self.saveContext()
}

applicationWillTerminate是名为UIApplicationDelegate的协议的委托方法。 当iOS应用即将终止(请参见其名称willTerminate)时,将调用此委托方法。 这是保存托管对象上下文的托管对象模型的未提交更改的最佳位置。

到目前为止,我们已经看到Xcode为我们完成了基本设置,包括创建与初始化AppDelegate文件中的核心数据堆栈有关的xcdatamodeled和重要的代码存根。

假设我们要创建一个Person实体并创建两个属性name和ssn。 所附的屏幕截图是我们如何做到的:

单击底部的添加实体按钮。

输入实体名称作为“人员”。

添加名称和ssn之类的属性。

这就是可视编辑器的外观。

设置实体和属性后,我们应该创建NSManagedObject子类。 有三种方法:

  • 手册/无(我将介绍)
  • 类定义(默认,非常容易,您可以自己学习)
  • 类别/扩展

手动/无

从顶部编辑器菜单中选择“创建NSManagedObject子类”。

点击下一步

点击下一步

点击创建

最后,我们手动生成了代码。

Xcode为所选位置中的每个所选实体创建并保存名为Person + CoreDataClassPerson + CoreDataProperties的文件。 Person + CoreDataClass实现 NSManagedObject 子类

Person + CoreDataProperties实现了Person + CoreDataClass扩展(对于Swift应用程序)或类别(对于Objective-C应用程序)。

Person — N​​SManagedObject子类

 进口基金会 
导入CoreData
  @objc(人) 
公共类人员:NSManagedObject {
  } 

扩展人员课程

 进口基金会 
导入CoreData

 扩展人员{ 
  @nonobjc公共类func fetchRequest()-> NSFetchRequest  { 
返回NSFetchRequest (entityName:“ Person”)
}
  @NSManaged公共变量名称:字符串? 
@NSManaged公共变量ssn:Int16
  } 

扩展包含获取请求方法,这是一个类方法。 因此,您无需创建Person的实例。 您可以直接调用Person.fetchRequest()。

此扩展名还包含带有@NSManaged关键字前缀的属性/属性。 @NSManaged 属性通知Swift编译器Core Data在运行时提供声明的存储和实现。 用最简单的术语来说,此关键字告诉编译器在运行时会有一些值。 @NSManaged与目标c中的@dynamic类似。

现在喝咖啡☕

让我们在情节提要中设置UI。 我已经将UIViewController嵌入UINavigation Controller中。 我向导航项目的左侧和右侧添加了条形按钮项目。 UIViewController包含一个UITableView。 最终设置如下所示:

接下来,连接IBAction以通过SSN删除并添加(+)。

设置应按照所附的屏幕截图进行:

确保还设置了tableView出口以及表视图的数据源和委托方法。

我们应该在表格视图中为该更改viewDidLoad方法注册单元格,如下所示:

 覆盖func viewDidLoad(){ 
super.viewDidLoad()
//加载视图后进行其他任何设置,通常是从笔尖进行。
tableView.register(UITableViewCell.self,
forCellReuseIdentifier:“单元格”)
}

创建一个存储的属性数组。 这将适用于在表格视图中显示数据。 它将包含人员对象。

  var people:[NSManagedObject] = [] 

当用户单击右上角的添加(+)按钮时,将调用添加名称功能。 如下更改addName:方法:

  @IBAction func addName(_ sender:Any){ 
  / *使用标题,消息和.alert样式初始化警报控制器* / 
 让alert = UIAlertController(title:“新名称”, 
消息:“添加新名称”,
preferredStyle:.alert)

/ *使用占位符“名称”创建名称文本字段* /
  alert.addTextField(configurationHandler:{(textFieldName)在 
textFieldName.placeholder =“名称”
})

/ *使用占位符“ ssn”创建一个ssn文本字段* /
  alert.addTextField(configurationHandler:{(textFieldSSN)在 

textFieldSSN.placeholder =“ ssn”
})
  / *创建保存操作* / 
 让saveAction = UIAlertAction(title:“ Save”,style:.default){ 

/ *查找文本字段的文本(名称)后卫,让其获取解包值,否则提早返回* /
 警卫让textField = alert.textFields ?. 
让nameToSave = textField.text else {
返回
}
  / *查找文本字段的文本(ssn)防护,让其获取展开值,否则提早返回* / 

警卫让textFieldSSN = alert.textFields?[1],
让SSNToSave = textFieldSSN.text else {
返回
}

/ *通过传递nameToSave和SSNToSave调用保存方法* /
self.save(名称:nameToSave,ssn:Int16(SSNToSave)!)
self.tableView.reloadData()
}

让cancelAction = UIAlertAction(title:“ Cancel”,
样式:.default)

alert.addAction(saveAction)
alert.addAction(cancelAction)

礼物(警告,动画:真实)
}

插入

下面的保存方法包含将数据保存在核心数据中的逻辑。 请通过下面的注释良好的代码:

  func save(name:String,ssn:Int16){ 
卫队让appDelegate = UIApplication.shared.delegate为? AppDelegate else {
返回
}


/ * 1。
在可以从核心数据存储中保存或检索任何内容之前,首先需要动手使用NSManagedObjectContext。 您可以将托管对象上下文视为用于处理托管对象的内存“便签本”。
可以考虑将新的托管对象保存到Core Data中的过程分为两个步骤:首先,将新的托管对象插入托管对象上下文中; 然后,对闪亮的新托管对象感到满意之后,您可以“提交”托管对象上下文中的更改,以将其保存到磁盘。
Xcode已经生成了一个托管对象上下文,作为新项目模板的一部分。 请记住,只有在开始时选中“使用核心数据”复选框,才会发生这种情况。 此默认托管对象上下文作为应用程序委托中NSPersistentContainer的属性存在。 要访问它,您首先需要获得对应用程序委托的引用。
* /
让managedContext = appDelegate.persistentContainer.viewContext

/ *
NSEntityDescription对象与特定的类实例相关联

NSEntityDescription
核心数据中实体的描述。

在此处检索具有给定名称的实体的人
* /
让实体= NSEntityDescription.entity(forEntityName:“人”,
在:managedContext)!


/ *
初始化托管对象并将其插入指定的托管对象上下文中。

初始化(实体:NSEntityDescription,
insertInto上下文:NSManagedObjectContext?)
* /
让person = NSManagedObject(entity:实体,
insertInto:managedContext)

/ *
手持NSManagedObject之后,您可以使用键值编码来设置name属性。 您必须完全按照数据模型中显示的方式拼写KVC密钥(在这种情况下为名称)
* /
person.setValue(名称,forKeyPath:“名称”)
person.setValue(ssn,forKeyPath:“ ssn”)

/ *
您可以将更改提交给person并通过在托管对象上下文上调用save来保存到磁盘。 注意save会引发错误,这就是为什么您在do-catch块中使用try关键字调用它的原因。 最后,将新的托管对象插入people数组,以便在重新加载表视图时显示该对象。
* /
做{
尝试managedContext.save()
people.append(人)
tableView.reloadData()
}将let错误捕获为NSError {
print(“无法保存。\(错误),\(error.userInfo)”)
}
}

实现表视图委托方法:

 扩展ViewController:UITableViewDataSource,UITableViewDelegate { 

func tableView(_ tableView:UITableView,
numberOfRowsInSection部分:Int)-> Int {
归还人
}

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

让人=人[indexPath.row]
让cell = tableView.dequeueReusableCell(withIdentifier:“ Cell”,
用于:indexPath)
cell.textLabel?.text = person.value(forKeyPath:“ name”)as? 串
返回单元
}
}

让我们运行代码来看看我们到目前为止所开发的内容。 我们已经了解了如何在核心数据中插入记录。 我们必须记住以下插入代码:

 让entity = NSEntityDescription.entity(forEntityName:“ Person”,in:managedContext)! 
 让person = NSManagedObject(实体:实体,insertInto:managedContext) 

让我们实现表视图的didSelectRow委托方法。 当用户单击该行时,我们将向用户显示警报,其中包含更新,删除和取消选项。

  func tableView(_ tableView:UITableView,didSelectRowAt indexPath:IndexPath){ 

/ *获取托管对象* /
让人=人[indexPath.row]

/ *初始化警报控制器* /
让警报= UIAlertController(标题:“更新名称”,
消息:“更新名称”,
preferredStyle:.alert)

/ *添加名称文本字段* /
alert.addTextField(configurationHandler:{(textFieldName)在

/ *在文本字段中将名称设置为plaveholder * /
textFieldName.placeholder =“名称”

/ *使用键值编码获取键“名称”的值,并将其设置为UITextField的文本。* /
textFieldName.text = person.value(forKey:“ name”)如? 串

})

/ *添加ssn文本字段* /
alert.addTextField(configurationHandler:{(textFieldSSN)在

/ *在文本字段中将ssn设置为plaveholder * /
textFieldSSN.placeholder =“ ssn”

/ *使用键值编码获取键“ ssn”的值,并将其设置为UITextField的文本。* /
  textFieldSSN.text =“ \(person.value(forKey:” ssn“)as?Int16 ?? 0)” 
})

/ *配置更新事件* /
让updateAction = UIAlertAction(title:“ Update”,style:.default){

警卫让textField = alert.textFields?[0],
让nameToSave = textField.text else {
返回
}

警卫让textFieldSSN = alert.textFields?[1],
让SSNToSave = textFieldSSN.text else {
返回
}

/ * imp部分,负责更新,将nameToSave和SSn传递给update:方法。* /
self.update(名称:nameToSave,ssn:Int16(SSNToSave)!,人:人为!人)

/ *最终重新加载表格视图* /
self.tableView.reloadData()

}

/ *配置删除事件* /
让deleteAction = UIAlertAction(title:“ Delete”,style:.default){

/ *查看删除方法的实现* /
  self.delete(person:与as一样的人!) 

/ *也从数组中删除人对象,以便数据源具有正确的数据* /
self.people.remove(at:(self.people.index(of:person))!)

/ *最后重新加载tableview * /
self.tableView.reloadData()

}

/ *配置取消操作* /
让cancelAction = UIAlertAction(title:“ Cancel”,
样式:.default)

/ *添加所有动作* /
alert.addAction(updateAction)
alert.addAction(cancelAction)
alert.addAction(deleteAction)

/ *最终存在* /
礼物(警告,动画:真实)
  } 

让我们实现更新方法如下:

  func update(name:String,ssn:Int16,person:Person){ 

卫队让appDelegate = UIApplication.shared.delegate为? AppDelegate else {
返回
}

/ * 1。
在可以从核心数据存储中保存或检索任何内容之前,首先需要动手使用NSManagedObjectContext。 您可以将托管对象上下文视为用于处理托管对象的内存“便签本”。
可以考虑将新的托管对象保存到Core Data中的过程分为两个步骤:首先,将新的托管对象插入托管对象上下文中; 然后,对闪亮的新托管对象感到满意之后,您可以“提交”托管对象上下文中的更改,以将其保存到磁盘。
Xcode已经生成了一个托管对象上下文,作为新项目模板的一部分。 请记住,只有在开始时选中“使用核心数据”复选框,才会发生这种情况。 此默认托管对象上下文作为应用程序委托中NSPersistentContainer的属性存在。 要访问它,您首先需要获得对应用程序委托的引用。
* /
让context = appDelegate.persistentContainer.viewContext

做{


/ *
手持NSManagedObject之后,您可以使用键值编码来设置name属性。 您必须完全按照数据模型中显示的方式拼写KVC密钥(在这种情况下为名称)
* /
person.setValue(名称,forKey:“名称”)
person.setValue(ssn,forKey:“ ssn”)

print(“ \(person.value(forKey:” name“))”)
print(“ \(person.value(forKey:” ssn“))”)

/ *
您可以将更改提交给person并通过在托管对象上下文上调用save来保存到磁盘。 注意save会引发错误,这就是为什么您在do-catch块中使用try关键字调用它的原因。 最后,将新的托管对象插入people数组,以便在重新加载表视图时显示该对象。
* /
做{
尝试context.save()
打印(“保存!”)
}将let错误捕获为NSError {
print(“无法保存\(错误),\(error.userInfo)”)
} {

}

} {
打印(“请求错误:\(错误)”)
}
}

最后,让我们也实现Delete。

  func delete(person:Person){ 
//假设类型具有对托管对象上下文的引用

//假设可以访问特定的NSManagedObject的objectID属性
//或者,可以提供足够精确的谓词表达式
//仅选择一个_single_实体

卫队让appDelegate = UIApplication.shared.delegate为? AppDelegate else {
返回
}
让managedContext = appDelegate.persistentContainer.viewContext

做{
/ *调用管理对象上下文的删除方法并在参数中传递人对象* /
managedContext.delete(人)

} {
//对错误情况做出响应
}

做{
尝试managedContext.save()
} {
//对错误情况做出响应
}
}

现在运行项目,我们应该能够插入,更新和删除实体。

现在应该是这样:

  func fetchAllPersons(){ 
卫队让appDelegate = UIApplication.shared.delegate为? AppDelegate else {
返回
}

/ *在使用Core Data进行任何操作之前,需要一个托管对象上下文。 * /
让managedContext = appDelegate.persistentContainer.viewContext

/ *顾名思义,NSFetchRequest是负责从Core Data进行提取的类。

使用init(entityName :)初始化获取请求,将获取特定实体的所有对象。 这是您在此处获取所有Person实体的操作。
* /
让fetchRequest = NSFetchRequest (entityName:“ Person”)

/ *您将获取请求移交给托管对象上下文以进行繁重的工作。 fetch(_ :)返回满足fetch请求指定条件的托管对象数组。* /
做{
人员=尝试managedContext.fetch(fetchRequest)
}将let错误捕获为NSError {
print(“无法获取。\(错误),\(error.userInfo)”)
}

}

因此,获取涉及在此处初始化对实体名称“ Person”的获取请求,然后将此获取请求传递给获取方法。 提取请求将返回人员对象数组[Person]。

从viewWillAppear调用fetchAllPerson()函数为:

 覆盖func viewWillAppear(_动画:布尔){ 
super.viewWillAppear(动画)
fetchAllPersons()
}

运行它之后,我们现在应该能够提取个人记录。

提取请求中的传递条件/谓词

假设我们要通过ssn删除人员。 我们可以在谓词的获取请求中传递ssn。

  @IBAction函数func deleteAction(_ sender:Any){ 

/ *使用标题和消息初始化警报控制器* /
let alert = UIAlertController(标题:“由ssn删除”,消息:“输入ssn”,preferredStyle:.alert)

/ *配置删除操作* /
让deleteAction = UIAlertAction(title:“ Delete”,style:.default){
警卫让textField = alert.textFields?.first,让itemToDelete = textField.text else {
返回
}
/ *传递ssn编号以删除(:)方法* /
self.delete(ssn:itemToDelete)
/ * reoad tableview * /
self.tableView.reloadData()

}

/ *配置取消操作* /
让cancelAciton = UIAlertAction(标题:“取消”,样式:.default)

/ *添加文本字段* /
alert.addTextField()
/ *添加动作* /
  alert.addAction(deleteAction) 
alert.addAction(cancelAciton)

目前(警告,动画:正确,完成:无)
}
func delete(ssn:String){
/ *获取对appdelegate文件的引用* /
卫队让appDelegate = UIApplication.shared.delegate为? AppDelegate else {
返回
}

/ *获取管理对象上下文的引用* /
让managedContext = appDelegate.persistentContainer.viewContext

/ *初始化获取请求* /
让fetchRequest = NSFetchRequest (entityName:“ Person”)

/ *使用NSPredicate传递您的条件。 我们只想删除符合我们条件的记录* /
fetchRequest.predicate = NSPredicate(格式:“ ssn ==%@”,ssn)
做{

/*managedContext.fetch(fetchRequest)将返回人员对象数组[personObjects] * /
让item =试试ManagedContext.fetch(fetchRequest)
为我在{

/ *调用删除方法(aManagedObjectInstance)* /
/ *这里我是托管对象实例* /
managedContext.delete(i)

/ *最后保存上下文* /
尝试managedContext.save()

/ *还更新您的数组* /
people.remove(at:(people.index(of:i))!)
  } 
  }将let错误捕获为NSError { 
print(“无法获取。\(错误),\(error.userInfo)”)
}

}

运行代码现在应该看起来像这样:

源代码

在下一部分中,我们还将编写一些单元测试用例:)。

快乐编码!

请通过评论支持我们,喜欢并分享我们的Facebook页面。

如果您想进行完整的iOS培训,可以通过以下详细信息与我联系,我会使用目标C或Swift提供实时iOS应用培训

skype:alok.upadhyay32

邮件:meiosdose@gmail.com

应用程式:+ 91–7838552946

谢谢。

Interesting Posts