一起养成写作习惯!这是我参加「日新计划 4 月更文应战」的第22天,点击查看活动概况。
今日职言:许多时分,失利并非自身能力缺乏,而是负面心情太多。心情稳定,是一个人最好的修养。
承接上一章的内容,咱们继续完结运用CoreData
结构建立一个简略的ToDo
待办事项App
。
这一章节,咱们正式进入运用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)")
}
})
}
}
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)
}
}
}
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
实体,这非常重要!非常重要!非常重要!重要的事情说三遍。
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)
}
}
}
ContentView.swift文件
界说好CoreData
模型类后,咱们需求在ContentView
主视图中运用它,咱们运用@FetchRequest
特点包装器从数据库加载数据
,咱们替换之前运用@State
标记的ToDoItem
数组数据来历。
@FetchRequest( entity: ToDoItem.entity(),
sortDescriptors: [ NSSortDescriptor(keyPath: \ToDoItem.priorityNum, ascending: false) ])
var todoItems: FetchedResults<ToDoItem>
咱们在环境中注入了保管目标上下文,获取所需的数据。
由于咱们的List
列表数据来历于新界说的todoItems
,咱们把整个ToDoListView
的body
内容拆回到ContentView
主视图,这样咱们就能够直接
遍历循环todoItems
的内容。
List {
ForEach(todoItems) { todoItem in
ToDoListRow(todoItem: todoItem)
}
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)
}
}
咱们要将新事项
插入数据库中,需求运用保管上下文创立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
优先级挑选视图,每当切换发生更改时,todoItem
的isCompleted
特点将被更新。咱们能够附加onReceive
修饰符,onReceive
修饰符能够监听isCompleted
特点和其他咱们界说好的特点的更改,并经过调用上下文的save
函数将它们保存到耐久存储区。
//监听todoItem数组参数改变并保存
.onReceive(todoItem.objectWillChange, perform: { _ in
if self.context.hasChanges {
try? self.context.save()
}
})
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)
}
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)
}
}
快来动手试试吧!
假如本专栏对你有帮助,无妨点赞、评论、关注~