一起养成写作习惯!这是我参加「日新计划 4 月更文应战」的第22天,点击查看活动概况。

今日职言:许多时分,失利并非自身能力缺乏,而是负面心情太多。心情稳定,是一个人最好的修养。

承接上一章的内容,咱们继续完结运用CoreData结构建立一个简略的ToDo待办事项App

这一章节,咱们正式进入运用CoreData结构完结数据耐久化

SwiftUI极简教程22:CoreData数据持久化框架的使用(下)

之前咱们是经过创立项意图时分勾选运用CoreData结构,系统给咱们创立了需求的文件模型。那么这一章节,咱们尝试运用CoreData结构到达数据耐久化的意图。

Persistence.swift文件

Persistence.swift文件是数据保存到耐久存储区的文件,经过将办理目标上下文注入到环境中,来完结在任何视图都能够检索上下文,并且能够办理数据

咱们删去多余的示例数据代码。

import Foundation
import CoreData
struct PersistenceController { 
static let shared = PersistenceController()
let container: NSPersistentContainer
init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "SwiftUICoreData")
if inMemory { container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores(completionHandler: { storeDescription, error in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
}
}

SwiftUI极简教程22:CoreData数据持久化框架的使用(下)

SwiftUICoreDataApp.swift文件

咱们回到SwiftUICoreDataApp.swift文件,咱们需求将保管目标上下文注入到环境中,这样咱们就能够方便地在内容视图中拜访上下文,并且办理数据库中的数据。

import SwiftUI
@main
struct SwiftUICoreDataApp: App {
let persistenceController = PersistenceController.shared
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
}

SwiftUI极简教程22:CoreData数据持久化框架的使用(下)

SwiftUICoreData.xcdatamodeld模型

模型创立完结后,咱们需求创立一个新的实体ToDoItem来存储咱们需求用到的参数,咱们将本来的实体Item从头命名为ToDoItem

ToDoItem实体中,咱们需求界说好项目需求的特点

id:UUID
name:String
priorityNum:Integer32
isCompleted:Boolean

由于priority优先级特点是一个Enum枚举类型,为了将Enum枚举类型保存到数据库中,咱们有必要存储它的原始值,即Int整数,因而需求运用Integer 32类型,并且为了避免命名抵触,咱们将特点命名为priorityNum

这儿再科普一个知识点。

由于ToDoItem实体咱们从头界说了,那么要确保Module模块要挑选CurrrentProductModule当前产品的模型,Codegen代码基因要挑选Manual/None,不然咱们在项目中引证模型的时分可能会找不到咱们界说的ToDoItem实体,这非常重要!非常重要!非常重要!重要的事情说三遍。

SwiftUI极简教程22:CoreData数据持久化框架的使用(下)

CoreData模型类

CoreData结构中,每个实体都与一个模型类配对,咱们需求在ToDoItem.swift文件界说模型类。

这儿CoreData的模型类继承自NSManagedObject协议,每个特点都运用@NSManaged进行注释,并且对应咱们ToDoItem创立的特点id、name、priorityNum、isCompleted

import Foundation
import CoreData
enum Priority: Int {
case low = 0
case normal = 1
case high = 2
}
public class ToDoItem: NSManagedObject {
@NSManaged public var id: UUID
@NSManaged public var name: String
@NSManaged public var priorityNum: Int32
@NSManaged public var isCompleted: Bool
}
extension ToDoItem: Identifiable {
var priority: Priority {
get {
return Priority(rawValue: Int(priorityNum)) ?? .normal
}
set {
self.priorityNum = Int32(newValue.rawValue)
}
}
}

SwiftUI极简教程22:CoreData数据持久化框架的使用(下)

ContentView.swift文件

界说好CoreData模型类后,咱们需求在ContentView主视图中运用它,咱们运用@FetchRequest特点包装器从数据库加载数据,咱们替换之前运用@State标记的ToDoItem数组数据来历。

@FetchRequest( entity: ToDoItem.entity(),
 sortDescriptors: [ NSSortDescriptor(keyPath: \ToDoItem.priorityNum, ascending: false) ])
var todoItems: FetchedResults<ToDoItem>

咱们在环境中注入了保管目标上下文,获取所需的数据。

由于咱们的List列表数据来历于新界说的todoItems,咱们把整个ToDoListViewbody内容拆回到ContentView主视图,这样咱们就能够直接遍历循环todoItems的内容。

List {
 ForEach(todoItems) { todoItem in
  ToDoListRow(todoItem: todoItem)
}

SwiftUI极简教程22:CoreData数据持久化框架的使用(下)

NewToDoView.swift文件

紧接着,咱们需求同步更新NewToDoView.swift的代码。要将一个新任务保存到数据库中,首要需求从环境中获取办理目标上下文。

@Environment(\.managedObjectContext) var context

然后,咱们在NewToDoView视图和saveButton保存按钮视图就不需求再@Binding绑定ToDoItem数组了。

@Binding var todoItems: [ToDoItem]

然后,咱们再更新下addTask增加新事项的办法。

//增加新事项办法
private func addTask(name: String, priority: Priority, isCompleted: Bool = false) {
let task = ToDoItem(context: context)
task.id = UUID()
task.name = name
task.priority = priority
task.isCompleted = isCompleted
do {
try context.save()
} catch {
print(error)
}
}

SwiftUI极简教程22:CoreData数据持久化框架的使用(下)

咱们要将新事项插入数据库中,需求运用保管上下文创立ToDoItem,然后调用上下文的save函数来提交更改。

由于咱们删去了todoItems绑定,所以还需求调整NewToDoView_Previews预览视图。

NewToDoView(name: "", priority: .normal, showNewTask: .constant(true))

由于咱们NewToDoView新建事项视图发现改变,咱们回到ContentView首页视图,从头修改下绑定联系。

NewToDoView(name: "", priority: .normal, showNewTask: $showNewTask)

相同,咱们在ContentView首页视图也需求从环境中获取办理目标上下文。

@Environment(\.managedObjectContext) var context

ToDoListRow视图

与增加新事项相似,咱们需求获取用于记载更新的保管目标上下文。关于Toggle优先级挑选视图,每当切换发生更改时,todoItemisCompleted特点将被更新。咱们能够附加onReceive修饰符,onReceive修饰符能够监听isCompleted特点和其他咱们界说好的特点的更改,并经过调用上下文的save函数将它们保存到耐久存储区。

//监听todoItem数组参数改变并保存
.onReceive(todoItem.objectWillChange, perform: { _ in
if self.context.hasChanges {
try? self.context.save()
}
})

SwiftUI极简教程22:CoreData数据持久化框架的使用(下)

deleteTask删去事项办法

上面咱们既然完结了addTask新增事项办法,那么顺便完结deleteTask删去事项的交互。

//删去事项办法
private func deleteTask(indexSet: IndexSet) {
for index in indexSet {
let itemToDelete = todoItems[index]
context.delete(itemToDelete)
}
DispatchQueue.main.async {
do {
try context.save()
} catch {
print(error)
}
}
}

deleteTask删去事项办法接纳一个存储要删去的项索引集,咱们只需求调用上下文的delete函数,并指定要删去的项,然后调用save函数来提交更新。

咱们把这个办法加到List里,就能够完结滑动删去的操作。

List {
 ForEach(todoItems) { todoItem in
  ToDoListRow(todoItem: todoItem)
 }.onDelete(perform: deleteTask)
}

SwiftUI极简教程22:CoreData数据持久化框架的使用(下)

preview预览作用

咱们运转模拟器的时分发现报错了,这是由于咱们没有在contentview_preview结构体中注入保管目标上下文。咱们需求创立一个内存中的数据存储,并预备一些测试数据。

咱们在Persistence.swift文件创立了一个PersistenceController实例,并将inMemory参数设置为true,然后咱们增加10个示例待办事项,并将它们保存到数据存储中。

import CoreData
struct PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentContainer
static var preview: PersistenceController = {
let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
for index in 0..<10 {
let newItem = ToDoItem(context: viewContext)
newItem.id = UUID()
newItem.name = "待办事项\(index)"
newItem.priority = .normal
newItem.isCompleted = false
}
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
return result
}()
init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "SwiftUICoreData")
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
}
}

之后咱们假如只需求在ContentView_Previews预览视图的上下文注入到内容视图的环境中,就能够看到示例的数据啦~

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
  ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}

SwiftUI极简教程22:CoreData数据持久化框架的使用(下)

快来动手试试吧!

假如本专栏对你有帮助,无妨点赞、评论、关注~