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文件包含与核心数据相关的应用程序生命周期方法和代码存根。 我们仅对核心数据感兴趣。 它包含了:
- 导入CoreData框架
- 初始化NSPersistentContainer类,然后初始化核心数据堆栈对象(托管对象模型,PersistentStoreCoordinator,托管对象上下文)。
- 一个名为saveContext()的方法。 它将托管对象模型保存到存储中。
- 当应用程序即将终止时,名为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中,托管对象上下文可以与两种并发模式一起使用,这两种并发模式由NSMainQueueConcurrencyType
和NSPrivateQueueConcurrencyType
定义。
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 + CoreDataClass和Person + CoreDataProperties的文件。 Person + CoreDataClass实现 NSManagedObject
子类 。
Person + CoreDataProperties实现了Person + CoreDataClass扩展(对于Swift应用程序)或类别(对于Objective-C应用程序)。
Person — NSManagedObject子类
进口基金会
导入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
谢谢。