Swift — 4 —核心数据—第3部分创建Singleton Core Data和重构插入,更新,删除操作

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

skype:alok.upadhyay32

邮件:meiosdose@gmail.com

应用程式:+ 91–7838552946

大家好😀。 在第2部分中,我们了解了基本的核心数据概念以及插入,更新和删除操作。 在开始对核心数据进行单元测试之前,我们必须松耦合代码。 最终将使我们的视图控制器类也变轻。 下载适用于Starter的第2部分代码。

在这一部分中,我们将在单独的类中分离与核心数据相关的业务逻辑。 最终源代码在本教程的结尾。

请输入☕和开始代码–

在您的项目中添加一个文件,并将其命名为“ CoreDataManager”。

复制以下代码并将其粘贴到“ CoreDataManager”。

 进口基金会 
导入CoreData
导入UIKit
 类CoreDataManager { 

// 1
静态让sharedManager = CoreDataManager()
// 2。
private init(){} //阻止客户端创建另一个实例。

// 3
懒惰的varpersistentContainer:NSPersistentContainer = {

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


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

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

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

让我们了解发生了什么事:

  1. 我们正在创建一个静态let ,以便sharedManager具有相同的实例并且不能更改
  2. 使用private关键字,这样就不会错误地初始化此类。 如果在某个地方您将尝试再次对其进行初始化,则会出现编译时错误。
  3. 初始化NSPersistentContainer ,从而延迟初始化完整的核心数据堆栈。 persistentContainer对象仅在需要时才被初始化。 我已经在第1部分中进行了详细介绍。
  4. 保存上下文方法会将我们未提交的更改保存在核心数据存储中。 我已经在第1部分中进行了详细介绍。

由于我们已将与核心数据相关的代码放在单独的文件CoreDataManager中,因此我们将从AppDelegate文件中删除所有与核心数据相关的代码。

  1. 删除导入的CoreData
  2. 删除懒惰的varpersistentContainer:NSPersistentContainer = {……………………}()
  3. 更改applicationWillTerminate如下:
  func applicationWillTerminate(_ application:UIApplication){ 
//在应用程序即将终止时调用。 如果合适,保存数据。 另请参阅applicationDidEnterBackground:。
//在应用程序终止之前将更改保存在应用程序的托管对象上下文中。
CoreDataManager.sharedManager.saveContext()
}

您的AppDelegate文件应如下所示:

 导入UIKit 
  @UIApplicationMain 
类AppDelegate:UIResponder,UIApplicationDelegate {
  var window:UIWindow? 

  func application(_ application:UIApplication,didFinishLaunchingWithOptions launchOptions:[UIApplicationLaunchOptionsKey:Any]?)->布尔{ 
//应用程序启动后进行自定义的替代点。
返回真
}
  func applicationWillResignActive(_ application:UIApplication){ 
//当应用程序即将从活动状态变为非活动状态时发送。 对于某些类型的临时中断(例如打来的电话或SMS消息),或者当用户退出应用程序并开始过渡到后台状态时,可能会发生这种情况。
//使用此方法可以暂停正在进行的任务,禁用计时器并使图形渲染回调无效。 游戏应使用此方法暂停游戏。
}
  func applicationDidEnterBackground(_ application:UIApplication){ 
//使用此方法释放共享资源,保存用户数据,使计时器无效以及存储足够的应用程序状态信息,以防您的应用程序在以后终止时恢复到当前状态。
//如果您的应用程序支持后台执行,则在用户退出时将调用此方法,而不是applicationWillTerminate:。
}
  func applicationWillEnterForeground(_ application:UIApplication){ 
//作为从后台到活动状态的过渡的一部分调用; 在这里,您可以撤消输入背景时所做的许多更改。
}
  func applicationDidBecomeActive(_应用程序:UIApplication){ 
//重新启动应用程序处于非活动状态时已暂停(或尚未启动)的所有任务。 如果该应用程序以前在后台运行,则可以选择刷新用户界面。
}
  func applicationWillTerminate(_ application:UIApplication){ 
//在应用程序即将终止时调用。 如果合适,保存数据。 另请参阅applicationDidEnterBackground:。
//在应用程序终止之前将更改保存在应用程序的托管对象上下文中。
CoreDataManager.sharedManager.saveContext()
}
}

看看现在有多少细长的AppDelegate文件looking

运行您的项目并查看错误😡

我们收到这些错误消息是因为我们更改了AppDelegate文件,而我们仍未在ViewController类中进行更改。

打开ViewController类并搜索

  appDelegate.persistentContainer.viewContext 

appDelegate.persistentContainer.viewContext替换为

  CoreDataManager.sharedManager.persistentContainer.viewContext 

构建并运行,它现在应该可以工作。 😀

我们仍然在ViewController中收到一些警告,因此我们需要删除与appDelegate相关的代码。

搜索并删除任何您发现以下代码的地方

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

构建并运行您的代码,它现在应该可以正常工作。

现在,我们需要在CoreDataManager类中编写insert,update,delete和fetch方法 。 那就是我们将重构ViewController类。

插入

从ViewController复制代码并将其粘贴到CoreDataManagerClass中。

  func save(name:String,ssn:Int16){ 

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

我们不需要tableView,people.append..blah之类的代码。

我们将对上面的代码进行细微的更改,下面是固定版本。

  func insertPerson(name:String,ssn:Int16)->人员?  { 

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

现在打开ViewController类并进行更改

  func save(名称:字符串,ssn:Int16) 

按照下面的代码:

  func save(name:String,ssn:Int16){ 
// 1
let person = CoreDataManager.sharedManager.insertPerson(name:name,ssn:ssn)
// 2
如果人!=无{
people.append(人!)// 3
tableView.reloadData()// 4
}
}

立即查看代码之美。 重量很轻。 是不是 :格林先生:

  1. 我们正在调用CoreDataManager类的insertPerson方法,它将返回一个可选的person对象。
  2. 在解包之前,我们必须确保从运行时崩溃中将其保存为nil。 如果不是零,则仅强行解开包装。 您还可以使用可选绑定来获取未包装的值。
  3. 在表视图数据源数组中添加此对象
  4. 最后重新加载表。

让我们运行它,看看一切正常。 是的,到目前为止一切都很好。

让我们重构更新代码

更新资料

在ViewController中搜索

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

方法。 我们将按原样复制此方法并将其粘贴到CoreDataManager类中。

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

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

}

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

现在打开ViewController文件并更改

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

方法按以下实现:

  func update(name:String,ssn:Int16,person:Person){ 
CoreDataManager.sharedManager.update(名称:名称,ssn:ssn,人:人)
}

看看代码人! 通过这种方法,我们减少了很多负担。 干杯🙂

构建它,插入一些数据并更新它,它应该可以工作。

现在,让更改删除实现并使CoreDataManager类中的删除可用。 从ViewController复制完整的delete(person 🙂函数及其实现,并将其粘贴到CoreDataManager类中。

  func delete(person:Person){ 


让managedContext = CoreDataManager.sharedManager.persistentContainer.viewContext

做{

managedContext.delete(人)

} {
//对错误情况做出响应
打印(错误)
}

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

解释很简单。 获取托管对象上下文。 调用delete方法并将person对象作为参数传递。 最后,调用do-try-catch来保存ManagedContext的未提交更改。

现在改变

  func delete(person:Person) 

实现如下:

  func delete(person:Person){ 
CoreDataManager.sharedManager.delete(person:person)
}

您在看这段代码😎。 重量轻。 让我们再次构建,运行和测试插入,更新和删除代码,一切正常。

最后,让我们快速重构fetchAllPersons()的代码,然后进行func delete(ssn:String)。

打开CoreDataManager类,然后粘贴以下代码:

  func fetchAllPersons(){ 


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

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

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

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

}

在上面的代码中进行小的更改:

  func fetchAllPersons()-> [Person]?{ 


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

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

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

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

}

现在我们将重构使用谓词的delete函数的代码。

将以下方法粘贴到CoreDataManager类中。

  func delete(ssn:String)-> [人员]?  { 
/ *获取对appdelegate文件的引用* /


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

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

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

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

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

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

/ *还更新您的数组* /
arrRemovedPeople.append(i as!Person)
}
返回arrRemovedPeople

}将let错误捕获为NSError {
print(“无法获取。\(错误),\(error.userInfo)”)
返回零
}

}

改变实施

 删除(ssn:字符串) 

在ViewController类中,如下所示:

  func delete(ssn:String){ 

让arrRemovedObjects = CoreDataManager.sharedManager.delete(ssn:ssn)
people = people.filter({(param)-> Bool in

if(arrRemovedObjects?.contains(param as!Person))!{
返回假
}其他{
返回真
}
})

}

说明—我们正在使用快速的高阶函数过滤器,从人员数组中删除arrRemovedObjects的对象。

  var array1 = [“ a”,“ b”,“ c”,“ d”,“ e”] 
让array2 = [“ a”,“ c”,“ d”]
array1 = array1.filter {!array2.contains($ 0)} //输出[b,e]

您也可以使用for循环create创建自己的逻辑。

让我们运行我们的代码并测试所有操作。 每件事都是完美的。

😉。

我也在这里写http://iosdose.com/wp/2018/03/29/swift-4-core-data-part-3-creating-a-singleton-coredatamanager-class-refactoring-insert-update-delete-操作/

如承诺的源代码在这里。

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

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

skype:alok.upadhyay32

邮件:meiosdose@gmail.com

应用程式:+ 91–7838552946

Interesting Posts