持续创作,加快生长!这是我参与「日新方案 10 月更文应战」的第2天,点击查看活动概况
coredata 是用于耐久化存储数据的,可以把它的作用简略理解为类似于前端浏览器的 localStorage。可是当你把 APP 删去的时分,APP 对应的 coredata 数据也会被删去。
本文旨在快速讲清 coredata 的开发运用。
通常有凭借 List 视图来解说 coredata 的运用,这是一种常用的方法。但除此之外,也应当有愈加通用的方法来运用 coredata,也便是下文将会解说的增修正查内容。
学会了以下的内容,即便脱离了 List ,你也能单独完结其中的某一项功用:
- 怎么装置 coredata
- 创立 coredata 实体和特点
- coredata 怎么增加数据
- coredata 怎么删去数据
- coredata 怎么查询数据
- coredata 怎么修正数据
话不多说,让咱们开端吧。
下文将会运用 Xcode 14、SwiftUI 开发。
装置 coredata
新建的项目装置 coredata
- 创立一个新项目
- 挑选 ios app
- 勾选 use Core Data
翻开项目的 HelloCoreData.xcdatamodeld 文件,可以看到现已默许创立了一个名为 Item 的实体。
到这儿,一个新项目装置 coredata 的部分就完结了。
现有项目装置 coredata
- 新建一个 Data Model 文件。
- 文件名一般和项目名称相同。
- 创立 Persistence.swift 文件
创立 Persistence,是为了让预览也能运用 coredata 数据;以下是一个官方模板,直接运用即可。
import CoreData
struct PersistenceController {
static let shared = PersistenceController()
static var preview: PersistenceController = {
let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
// 给预览增加预设数据
for _ in 0..<10 {
let newItem = Item(context: viewContext)
newItem.timestamp = Date()
}
do {
try viewContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
return result
}()
let container: NSPersistentContainer
init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "HelloCoreData")
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
}
}
创立 coredata 实体和特点
在上文装置和装备好 coredata 后,接下来就可以创立实体和相应的特点了。
创立实体
在这儿咱们创立一个名为 User 的实体。
实体的名称一般选用首字符大写驼峰的命名方法。
增加了 3 个特点。
创立实体模型
创立完实体后,咱们还需求创立一个实体对应的模型,Xcode 也提供了主动生成实体模型的功用,但这儿咱们选用手动创立实体的方法。
- 首要设置 User 实体的 Class 特点。
- 然后创立一个模型:Models/User.swift
import Foundation
import CoreData
final class User: NSManagedObject {
@NSManaged var id: UUID
// 用户名
@NSManaged var name: String
// 喜好
@NSManaged var hobby: String
}
创立视图
接下来开端完结UI页面的编写。
创立一个名为 UserList.swift 文件。
咱们会从 ContentView.swift 运用 NavigationLink 导航到 UserList.swift 页面。
先给预览装备 .environment 修饰符,这样预览后续才干正确显现 coredata 数据。
再新增一个 @Environment 特点,下面的增修正查功用都会用到 viewContext。
只需你在视图中操作 coredata,根本都需求设置 @Environment 特点。
import SwiftUI
struct UserList: View {
@Environment(\.managedObjectContext) private var viewContext
var body: some View {
Text("Hello, World!")
}
}
struct UserList_Previews: PreviewProvider {
static var previews: some View {
UserList()
.environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
到这儿,根本的准备作业和 UI 咱们都已完结,接下便是实际操作了。
此时预览还不能正常显现,下面咱们会修正这个问题。
coredata 查询数据
为了在 UserList 视图中显现用户列表数据,咱们需求运用 @FetchRequest 来获取数据:
import SwiftUI
struct UserList: View {
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(
entity: User.entity(),
sortDescriptors: [NSSortDescriptor(keyPath: \User.id, ascending: false)],
animation: .default)
// 这便是咱们获取到 coredata user 的数据
private var userList: FetchedResults<User>
var body: some View {
Text("Hello, World!")
}
}
struct UserList_Previews: PreviewProvider {
static var previews: some View {
UserList()
.environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
接着运用 ForEach 来显现数据:
struct UserList: View {
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(
entity: User.entity(),
sortDescriptors: [NSSortDescriptor(keyPath: \User.id, ascending: false)],
animation: .default)
private var userList: FetchedResults<User>
var body: some View {
VStack {
if userList.isEmpty {
Text("暂无用户数据")
} else {
ForEach(userList, id: \.self) { item in
HStack {
Text(item.name)
Text("喜好:\(item.hobby)")
}
}
}
}.navigationTitle("用户列表")
}
}
但现在预览还无法正常显现,咱们需求在 Persistence.swift 文件中给预览增加一些数据用于显现:
for index in 0..<10 {
let newItem = Item(context: viewContext)
newItem.timestamp = Date()
// 增加的预览数据
let userItem = User(context: viewContext)
userItem.id = UUID()
userItem.name = "用户 \(index)"
userItem.hobby = "篮球"
}
查询数据部分就完结了,是不是很简略。
不管你在哪一个视图中运用了 coredata 数据,要想让该视图正常预览,都需求在 Persistence.swift 增加相应的预览数据。当然,就算不增加预览数据,也不影响模拟器启动。
coredata 新增数据
在新增数据前,咱们先简略增加一些UI控件。
下面的代码中将会省掉预览的 UserList_Previews 代码。
自定义导航栏,增加一个返回按钮和新增按钮:
import SwiftUI
struct UserList: View {
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(
entity: User.entity(),
sortDescriptors: [NSSortDescriptor(keyPath: \User.id, ascending: false)],
animation: .default)
private var userList: FetchedResults<User>
@State private var showAdd = false
var body: some View {
VStack {
if userList.isEmpty {
Text("暂无用户数据")
} else {
ForEach(userList, id: \.self) { item in
HStack {
Text(item.name)
Text("喜好:\(item.hobby)")
}
}
}
}
.padding()
.navigationTitle("用户列表")
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: Button(action : {
}){
//按钮及其款式
Image(systemName: "chevron.left")
}, trailing: Button(action : {
self.showAdd = true
}){
Image(systemName: "plus")
})
}
}
创立表单和数据字段:
import SwiftUI
struct UserList: View {
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(
entity: User.entity(),
sortDescriptors: [NSSortDescriptor(keyPath: \User.id, ascending: false)],
animation: .default)
private var userList: FetchedResults<User>
@State private var showAdd = false
// 表单绑定数据
@State private var name: String = ""
@State private var hobby: String = ""
func addUser() {
}
var body: some View {
VStack {
if showAdd {
VStack {
TextField("用户名", text: $name)
.padding()
.border(Color.gray)
TextField("喜好", text: $hobby)
.padding()
.border(Color.gray)
Button(action: {
addUser()
}, label: {
Text("保存")
.padding(.horizontal, 2)
.padding(.vertical, 15)
.frame(maxWidth: .infinity)
.background(Color.blue)
.foregroundColor(.white).cornerRadius(24)
})
}.padding()
}
if userList.isEmpty {
Text("暂无用户数据")
} else {
ForEach(userList, id: \.self) { item in
HStack {
Text(item.name)
Text("喜好:\(item.hobby)")
}
}
}
}
.padding()
.navigationTitle("用户列表")
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: Button(action : {
}){
//按钮及其款式
Image(systemName: "chevron.left")
}, trailing: Button(action : {
self.showAdd = true
}){
Image(systemName: "plus")
})
}
}
UI准备作业完结,现在新增数据时,咱们需求通过 viewContext 获取到实体,然后给实体的特点赋值。
func addUser() {
withAnimation {
if !name.isEmpty && !hobby.isEmpty {
let newItem = User(context: viewContext)
newItem.id = UUID()
newItem.name = name
newItem.hobby = hobby
// ...
}
}
}
最终,保存上下文。
调用 save() 保存数据很重要,如果不保存,即便数据新增成功,数据是没有真实存储到内存中的。
func addUser() {
withAnimation {
if !name.isEmpty && !hobby.isEmpty {
let newItem = User(context: viewContext)
newItem.id = UUID()
newItem.name = name
newItem.hobby = hobby
do {
try viewContext.save()
showAdd = false
} catch {
let nsError = error as NSError
// fatalError() 使应用程序生成崩溃日志并停止。 尽管此功用在开发过程中可能很有用,不该在出产应用程序中运用此功用。
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
}
演示效果:
到这儿,新增数据部分完结。
tips: 在模拟器输入中文:在设置-通用-语言与地区中增加简体中文的语言,在设置-通用-键盘增加简体中文输入法后,在模拟器中按 Command + K,调起软键盘,点击软键盘下面那个小地球仪,切换成中文输入,就能在模拟器中输入中文了。
coredata 删去数据
先来增加一个删去提示框,首要创立一个操控显现提示框的变量和用于存储被删去数据id的变量。
@State private var isDelete = false
@State private var deleteId: UUID = UUID()
// ...
然后增加删去按钮和提示框,保存被删去数据的 id。
if userList.isEmpty {
Text("暂无用户数据")
} else {
ForEach(userList, id: \.self) { item in
HStack {
Text(item.name)
Text("喜好:\(item.hobby)")
Button(action: {
isDelete = true
// 点击删去时保存要删去的数据的 id
self.currentUserId = item.id
}, label: {
Text("删去")
})
.alert("提示", isPresented: $isDelete) {
Button(role: .cancel) {
isDelete = false
} label: {
Text("撤销")
}
Button(role: .destructive) {
} label: {
Text("删去")
}
} message: {
Text("确定删去吗?")
}
}
}
}
增加删去函数,该函数依据传递的 id 参数找到需求被删去的数据,然后传递给 viewContext.delete 函数,删去后保存即可。
func deleteUser(id: UUID) {
if let record = userList.first(where: { $0.id == id }) {
withAnimation {
viewContext.delete(record)
do {
try viewContext.save()
isDelete = false
} catch {
isDelete = false
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
}
调用 deleteUser 函数:
Button(role: .destructive) {
deleteUser(id: currentUserId)
} label: {
Text("删去")
}
在这儿我现已事先新增了3条数据,来看看完结的效果:
coredata 修正数据
修正时分会用到上一步创立的表单和数据。
再新增一个变量操控修正:
@State private var showUpdate = false
在文字旁边新增一个修正按钮,一起将当前要修正的数据对象的值赋值给表单绑定值,让表单显现对应数据。
Text(item.name)
Text("喜好:\(item.hobby)")
Button(action: {
showUpdate = true
// 赋值
self.name = item.name
self.hobby = item.hobby
self.currentUserId = item.id
}, label: {
Text("修正")
})
当 showUpdate 为 true 时显现表单,以及切换为调用 updateUser 函数:
if showAdd || showUpdate {
VStack {
TextField("用户名", text: $name)
.padding()
.border(Color.gray)
TextField("喜好", text: $hobby)
.padding()
.border(Color.gray)
Button(action: {
if showAdd {
addUser()
} else if showUpdate {
updateUser(id: currentUserId)
}
}, label: {
Text("保存")
.padding(.horizontal, 2)
.padding(.vertical, 15)
.frame(maxWidth: .infinity)
.background(Color.blue)
.foregroundColor(.white).cornerRadius(24)
})
}.padding()
}
编写 updateUser 函数:
func updateUser(id: UUID) {
if let record = userList.first(where: { $0.id == id }) {
withAnimation {
record.name = self.name
record.hobby = self.hobby
do {
try viewContext.save()
showUpdate = false
} catch {
showUpdate = false
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
}
来看最终效果:
在上面的几个操作中,省掉了一些数据清理作业,比方新增后的数据置空,表单的躲藏等。
总结
本文咱们完结了 coredata 的增修正查等根本操作,信任通过学习你现已根本把握了这些内容,coredata 还有许多运用方法,比方更细粒度的查询,分页等操作,有时机咱们再讲吧。
这是 SwiftUI 开发之旅专栏的文章,是 swiftui 开发学习的经验总结及实用技巧共享,欢迎关注该专栏,会坚持输出。一起欢迎关注我的个人大众号 @JSHub:提供最新的开发信息速报,优质的技术干货引荐。或是查看我的个人博客:Devcursor。
点赞:如果有收成和帮助,请点个赞支撑一下!
保藏:欢迎保藏文章,随时查看!
评论:欢迎评论交流学习,共同进步!