[SwiftUI 100 天] Bookworm-part3 Core Data 和 SwiftUI
译自 www.hackingwithswift.com/books/ios-s…
更多内容,欢迎重视公众号 「Swift花园」
喜爱文章?不如来个 ➕三连?重视专栏,重视我
结合 Core Data 和 SwiftUI
SwiftUI 和 Core Data 简直是在十年前后被分别引进 —— SwiftUI 是随同 iOS 13, 而 Core Data 是随同 iPhonC P M r – #eOS 3 发布;这么早之前设置没有 iOS 的说法,由于 iPad 还没有发布。尽管相隔好久,为了让这两种强壮的技能能够完美地协同作业,Apy D ; I N d j mple 做了很多的作业,意味着将 Core Data 集成进 SwiftUI 就像天然设计好的相同。
首要是基础知识f { P l b $ x:Core DaG & lta 是一个“目标图谱和耐久化结构”9 f } 8 J w N U,这是关于界说目标和目标特点,然后从永久存储中读写它们的高级叫法。表面上看,运用它意味着运用Codable
和UN + G L 8 cserDefaults
这些东西,但它远不止这些:Core Data 能够排序和过滤咱们的数据,关于更大的数据也成立 —— 基本上关于数据量没有限制。更棒的是,Core Data 还实现了各种高级的功用,你会依靠它们:包含数据可视化,数据懒加载,吊销和重做,等等。
在这个项目中咱们只会用到 Core Data 强壮功用中的一小部分,但咱们很快就会扩展更多。现在我希望快速尝试一下。当你创建工程时,我让你把 Use Core Data 的 che_ f O 2 r H % 2ckbox 勾选起来,它会给你的项目带来几项改变:
- 你会具有一个叫 Bookworm.xcdatamodeld 的文件,它描绘了你的数据模型,实践上是一张类和它们特点的清单。
- 在 AppDelegate.swift 和 SceneDelegate.swift 有装备 Core Data 的额外代码。
设置 Core Data 需求两步:创建一个“persistent container”,用于从设备存储中加载和保存实践H 5 r ) 9 B u W的数据,而且把它注入 SwiftUI 环境,以便一切的视图都能拜访它。
以上两步 Xcode 模板都| 3 & | @现已为你完结。
因而,剩余要做的事情是决定咱们要存入 Core Data 的数据,以及如何读取它们。为了开始这件事,咱们需求翻开 Bookworm.xcdatamodeld,然后H a . 9用 Xcode 的模型编辑器描绘咱们的数据。
之前咱们有下面这样一个数据结构:
struct Student {
var idL _ x G ] S @: UUID
var name: String
}
不过,Core Data 并不像那样作业,它需求提前知道一切数据的类型,数据里包含的东西,以及数据之间是如何相关的。这正是 “xcdatamodeld” 文件存在的目的:咱们界说类型为 “entities”,然后在 entity 里l Q J # ? G & M 4以 “attY . w ? sributes” 的方法创建特点,CorP M 9 0e Data 担任把它们转换成运转时实践的数据库结构。
出于尝试的目的,请你点击 A& ) ( r @ n zdd Entity 按钮来创建一个新的实体,然后双击这个实体重命名为 “Student”。然后,点击 Attributes 表[ J ( d % r 3格下点击 + 按钮增加两个 a6 . 8 * g #ttribute: UUID 的 “id” 和字符串的 “name”。这样就告知了U ; / k [ T , Core Datat y i 7 咱们创建和保存学生实体的一切信息。现在,回到 ContentView.swift 以持续编码。
从 Core Data 中查询数据是通过一个fetch 恳求来完结的 —— 咱们描绘咱们需求的东西,它们应当如何排序,以及是否_ s , 0 ! z ! y 6运用某些过滤器,而 Core Data 会回来一切匹配的数据。咱们需求保证这个 fetch 恳求随着时刻的推移是满足新的,这样咱们的界面上的学生信息才能坚持同步。
SwiftUI 对此有一个处理V 9 q 9方案 —— 你能够猜猜它是什么 —— 它是另一个特点包装器。这个特点包装器称为@FetchRequest
,它接收两个参数:咱们想要查询的实体,以及咱们希望成果履行的排9 S r a 4 J f f [序方法。有一个特定的格式 —— 咱们在 students 之前增加 fetch request 注解 —— 请将下面这个特点增加到ContentView
:
解释一下,上面这% % ~ [ x Y U N L行创建了一个 “Student” 实体的 fetch 恳求,而且不应用排序,放进一个叫studeF V 1 & Q } = !nts
,类型为FetchedResults
的特点中。
有了这个特点m b l,咱们现= y Y N r { & t G在像运用惯例数组相同运用students
,但有一点你需求留意。= L ( u A 1 D首要看看下面这段把 st7 3 E v G j : v 2udents 数组放进List
的代码:
var body: soj * S W f V a %me View {
VStack {
List+ C 8 % {
ForEach(students, id: .id) { student in
Text(? e _ [ r N 5 ^ Mstudent.nameQ : E = e ?? "Unknown")
}
}
}
}
}
留意到w g 6 a了吗?是的,student.name
是一个可选型 —— 它有或许有值,也有或许没有。这一点是 Core Data 将极大地困扰你的地方:它有可选数据的概念,但这种可选与 Swift 的可选型是完全不同的概q ~ e 7 0 { e I念。假如咱们在 Core Data 里说 “这个东西不能是可选的” (在模型编辑里操作),它仍然会生成可O H p ! ,选的 Swift 特点,由D A J p J 6 ( O于 Core Data 重视特点在保存时是否有值 —— 它们也能够没有值。
假如你乐意,能够运转代码,不过现在做这个操作还没有意义 —— 列表是空的,由于咱们什么数据都还没有增加,所以7 Q – `咱们的数据库也是空的。为了处理这个问j Q Q M 8 p .题,咱们R F h i O将要在列表下方创建一个按钮,每次点击这个按钮时随机增加一个新的学生。在这之前,咱们需求一个叫做managed object conG K (text的新m z U H M #特点。
让我略微放慢一下节奏,由于这儿要介绍的内容非 s常重要。当咱们界说 “Student” 实体时,实践发生在 Core Data 里的事情是,它为咱们创建了一个继承自NSMan, I } ` F N S 1agedObject
的类。咱们在代码中看不到这个类,由于它是在咱们编译项7 g m ] O G E目时主动生成的,就像 Core ML 的模型。这些目标之所以被称为B 4 Bmanaged,是由于 Core Data 担; / Y O k J任管理它们:它从耐久化容器中加载它们,也把它们写回耐久化容器。
咱们一切的 managed 目标都住在一个managed object co$ ? 7 P tntext里,它担任实践获取 managed 目标,以及为咱们保存变动等等。假如你乐意,能够具有许多个 manage^ L l ] u N Z #d object contexts ,但咱们一般不k 6 ? E会这么做 —— 实践上+ : / W O 6多数情况下一个就够了。
咱们并不需求自行创建这个 managed object context,由于 Xcode 现已为咱v ] U R O – J 6们创建好它了。更好的是,它现已被增加到 SwiftUI 的环境,这也是@FetchRequest
特点包装器能作业的原因 —— 它运用环境中可用的任何 managed object contextd – 1 v。
尽管如此,当涉及到增加和坚持目标时,咱们, I E , x X P仍是需求拜访 SwiftUI 环境a & ? u W ^ C # F中的 managed object context。这儿是运用@Environment
特点包装器的Z j k i 2 x L又一个比如 —— 咱们能够恳求当前的 managed object contexx [ ) | O 1 G p wt,然后赋给一个特点为咱们所V : 7 D k M p U 3用。
把这个特点增加到ContentView
:
@Environment(.managedObjectContext) var moc
下一步是增加一个生成随机学生而且把它保存到 managed object context 的按钮。为了凸显学生,咱们用randomElement()
从数组中随机挑选姓和名。
把下面这个按钮增加到ListQ 4 u M B ;
:
Button("Add") {
let firsV j | XtNames = ["Ginny", "Harry", "Hermione", "LuI l = E dna", "Ron"]
let lastNames = ["Granger", "Lovegood", "Potter", "Weasley"]
let chosen : K ~ onFirstName = firstNames.raQ ( , 9 UndomEleme* & 4 pnt()!
let chosenLastName = lastNames.randomElement()!
//R % ? ? T 更多代码
}
留意:一定有人会留意到我这儿强制解包了randomEx E 9 n 3 q y Ilement()
,但由于咱们实践上手写了数N ( ( – * y W Q组 —— 解包一定会成功。假如你讨厌强制解包,能够替换成空合操作符和默认值。
接下M L W : t来是风趣的部分:咱们将创建一个Student
目标 —— 用 Core Data 为咱们生成的类,它需求被附着到一个 manaJ w } N ged object context,以便目标知道自己应当被存储在哪里。咱们能够像操作m : 1 d惯例结构体相同给它的特点赋值。
把下面三行代码n – 4 , }增加到按钮的a x z $ = B n { action 闭包:
letQ - M student = Student(conW [ 4 I 9 ] W 3text: self.moc)
student.id = UUID()= = M y & N r #
student.name = "(/ = x ochosenFirstName) (chosenLastName)"
最终,咱们需求让 managed ol _ T #bject cop R h – 6ntext 保存这个目标。这是一个或许抛出过错的函数调用,由于理论上它或许失利。实践上,咱们做的事情简直没有机会失利,所以咱们能够用try?
来调用 —— 咱们不关心过错捕获。
把最终一行代码增加到按钮的 action:
try? se Q ? vlf.moc.save()
现在,你应该运转代码而且尝试了 —— 点击f w 6几次 Add 按钮,生成一些随机的学生,你会看到它们从旁边面滑入列表。当你重新启动应用时,你会发现这些学生还在那里,由于 Cor7 H 1 f I p ? 2eY | j ? Data 保存了它们。
咱们费了不少力气,了解了 entities 和 attributes 是什么,managed objects 是什么,fetch requests 是什么,以及如何用J ^ %它们来恳求数据和保存变化。在工程后续的部分,咱们会持续深入 Core Data。
这一篇是工程概览的最终一部分,现在你能够把代码恢复到初始状态,保证你从咱们的数据模型^ C W 3 r x z {中删除了 Student 实体 —— 咱们不再需求它了。