版本记录
版本号 | 时间 |
---|---|
V1.0 | 2020.10.19 星期一 |
前言
数据的持久化存储是移动端不可避免的一个问题,很多时候的业务逻辑都需要我们进行本地化存储解决和完成,我们可以采用很多持久化存储方案,比如说
plist
文件(属性列表)、preference
(偏好设置)、NSKeyedArchiver
(归档)、SQLite 3
、CoreData
,这里基本上我们都用过。这几种方案各有优缺点,其中,CoreData是苹果极力推荐我们使用的一种方式,我已经将它分离出去一个专题进行说明讲解。这个专题主要就是针对另外几种数据持久化存储方案而设立。
1. 数据持久化方案解析(一) —— 一个简单的基于SQLite持久化方案示例(一)
2. 数据持久化方案解析(二) —— 一个简单的基于SQLite持久化方案示例(二)
3. 数据持久化方案解析(三) —— 基于NSCoding的持久化存储(一)
4. 数据持久化方案解析(四) —— 基于NSCoding的持久化存储(二)
5. 数据持久化方案解析(五) —— 基于Realm的持久化存储(一)
6. 数据持久化方案解析(六) —— 基于Realm的持久化存储(二)
7. 数据持久化方案解析(七) —— 基于Realm的持久化存储(三)
8. 数据持久化方案解析(八) —— UIDocument的数据存储(一)
9. 数据持久化方案解析(九) —— UIDocument的数据存储(二)
10. 数据持久化方案解析(十) —— UIDocument的数据存储(三)
11. 数据持久化方案解析(十一) —— 基于Core Data 和 SwiftUI的数据存储示例(一)
12. 数据持久化方案解析(十二) —— 基于Core Data 和 SwiftUI的数据存储示例(二)
13. 数据持久化方案解析(十三) —— 基于Unit Testing的Core Data测试(一)
14. 数据持久化方案解析(十四) —— 基于Unit Testing的Core Data测试(二)
开始
首先看下主要内容:
在本
SwiftUI Realm
教程中,您将通过构建购物清单应用程序来学习如何将Realm
与SwiftUI
一起用作数据持久性解决方案。内容来自翻译。
下面看下写作环境:
Swift 5, iOS 14, Xcode 12
接着就是正文啦
Realm Mobile Database是一种流行的对象数据库管理系统。它是开源的,可以在多个平台上使用。 Realm
的目标是成为一种持久,快速,高性能,灵活和简单的解决方案,同时仍可编写类型安全的Swift
代码。
SwiftUI
是Apple最新,最热门的UI
框架。它使用声明性语法来使用Swift代码构建视图。当用户与其交互时,它依靠状态states
来被动地更新其视图。由于Realm
使用的Live Objects
也会自动更新,因此混合使用这两个框架很有意义!
在本SwiftUI领域教程中,您将学习如何:
- 设置
Realm
- 定义数据模型
- 对对象执行基本的
CRUD
操作 - 将更改从数据库转到用户界面
- 数据模型更改时处理迁移
您将通过在应用程序中实现Realm
数据库来学习所有这些知识。
注意:本
SwiftUI Realm
教程假定您已经熟悉SwiftUI
。如果您刚刚开始使用SwiftUI,请查看我们的SwiftUI video course。
打开启动文件夹中的PotionsMaster.xcodeproj
。
PotionsMaster
是一款可满足您所有制药需求的应用程序。 药水冲泡是一项具有挑战性的技能。 搅拌技术,时间安排和装瓶可能是艰巨的任务,即使对于经验丰富的技师也是如此。 这个应用可协助您追踪所需的食材和已购买的食材。 使用PotionsMaster
,即使您一直在阅读的那个困难的新药也将变得轻而易举!
现在该开始工作了。 首先,构建并运行。
PotionsMaster
是一个简单的应用程序,可让您添加,更新和删除列表中的成分。但是当您使用该应用程序时,会发现一个小问题。无论您做什么,该应用程序都不会保留您的数据!实际上,当您尝试创建,更新或删除成分时,它不会执行任何操作。但请放心,您将使用Realm
使其发挥作用。
1. Project Structure
在深入研究并修复应用程序以开始酝酿下一个药水之前,请仔细阅读入门项目。它包含以下密钥文件:
- Ingredient.swift:此结构表示一种成分。
-
IngredientStore.swift:这是商店模式
(Store Pattern)
的类实现。此类负责存储配料。您还可以在其中对这些成分进行操作。 -
IngredientsListView.swift:这是应用程序的主视图。此类显示一个列表
List
。有一个区域Section
供您购买原料,另一个区域供您购买原料。 -
IngredientFormView.swift:您将使用此
Form
来创建和更新成分。
该项目使用Store Pattern来处理状态并将更改传播到UI。 IngredientStore
提供了一份食材清单,包括您需要购买的食材和已经购买的食材。当用户与应用交互时,动作会改变状态state
。然后,SwiftUI
收到一个信号,以用新状态更新UI。
Working with Realm
Realm
旨在解决当今应用程序的常见问题。它提供了许多优雅,易于使用的解决方案。它可用于多个平台,包括但不限于:
Swift/Objective-C
Java/Kotlin
JavaScript
.NET
关于Realm
的最酷的部分是这些技能是可以转让的。一旦以一种语言学习了Realm基础知识,就很容易用另一种语言来学习它们。而且由于Realm
是跨平台的数据库,因此它的API
从一种语言到另一种语言的更改不会太大。
不过,Realm
并不是万能的数据库。与SQLite
不同,Realm
是NoSQL
对象数据库。像任何其他NoSQL
数据库一样,它也有优点和缺点。但是Realm
是使多平台团队保持同步的绝佳选择。
1. Understanding the Realm Database
在开始设置Realm
之前,您需要了解一些工作原理。
Realm
使用文件来保存和管理数据库。应用中的每个Realm
数据库都称为一个realm
。您的应用程序可能有多个realm
,每个realm
都处理不同的对象域(domain of objects)
。这有助于使您的数据库井井有条,简洁明了。而且,由于跨平台可用,因此您可以在iOS
和Android
等平台之间共享预加载的Realm
文件。真的很有帮助,对吧?
要打开Realm
文件,只需实例化一个新的Realm
对象。如果您未传递自定义文件路径,则Realm
会在iOS上的Documents
文件夹中创建default.realm
文件。
有时,您可能想使用一个realm
而不实际在磁盘上写入数据。该数据库为以下情况提供了便捷的解决方案: in-memory realms
。这些在编写单元测试时很有用。您可以使用in-memory realms
来为每个测试用例预加载,修改和删除数据,而无需实际在磁盘上进行写操作。
Realm Mobile Database
不是Realm
提供的唯一产品。该公司还提供Realm Sync,这是一种用于在多个设备之间以及在云中同步Realm
数据库的解决方案。此外,Realm提供了一个很棒的应用程序来打开,编辑和管理您的数据库:Realm Studio。
Setting up Realm
要开始使用Realm
,您必须将其作为依赖项包含在您的项目中。 您可以使用许多依赖项管理工具,例如Swift Package Manager
和CocoaPods
。 本教程使用Swift Package Manager
,但请随意使用您更喜欢的工具。
注意:如果您不熟悉
Swift Package Manager
,或者想了解更多信息,请查看我们的Swift Package Manager for iOS tutorial教程。
要设置您的依赖关系,请选择File ▸ Swift Packages ▸ Add Package Dependency…
。 复制以下内容并粘贴到组合的搜索/输入框中:
这是包含Realm
软件包的GitHub
存储库的位置。 点击Next
。
接下来,Xcode
要求您为此程序包定义一些选项。 只需保留Up To Next Major
的默认值即可使用Realm
的最新版本。 (因此,在撰写本文时,此值从5.4.0
(含)到6.0.0
(不含)。)单击Next
。
最后,选择应添加到项目中的包装产品和target
。 选择Realm
和RealmSwift
,然后单击Finish
。Xcode
从存储库下载代码,并将Realm
包添加到PotionsMasrer target
。
构建并运行以确保一切正常。
设置好Realm
之后,就可以创建第一个data model
了。
Defining Your Realm Data Model
要构建成分模型,请在Models
组中添加一个新的Swift
文件,并将其命名为IngredientDB.swift
。 添加以下代码:
import RealmSwift
// 1
class IngredientDB: Object {
// 2
@objc dynamic var id = 0
@objc dynamic var title = ""
@objc dynamic var notes = ""
@objc dynamic var quantity = 1
@objc dynamic var bought = false
// 3
override static func primaryKey() -> String? {
"id"
}
}
这是您刚添加的代码的明细:
- 1) 首先,定义您的类,继承自
Object
,Realm
是所有数据模型的基类。您将使用该类将成分保存在Realm
中。 - 2) 在这里,您定义要
Realm
存储的每个Ingredient
属性。 - 3) 最后,您重写
primaryKey()
告诉Realm
哪个属性是模型的主键。Realm
使用主键来强制唯一性。主键还提供了一种获取和更新数据的有效方法。
就这些!
您将Realm
数据模型定义为常规Swift类。 Realm使用这些类在磁盘上写入数据,但是对Object
子类有一些限制。由于跨平台性质,Realm
仅支持有限的一组与平台无关的属性类型。其中一些属性是:
Bool
Int
Double
Float
String
Date
Data
Optional
属性具有特殊限制。您可以将String,Date
和Data
声明为可选。其余部分,使用包装器类RealmOptional
;否则,它们必须具有值。
每个属性都有@objc dynamic
关键字。这使得它们可以在运行时通过Dynamic Dispatch
访问。 Realm
使用此Swift
和Objective-C
功能在读取和写入数据之间创建外观。访问属性时,Swift
委托Realm
负责为您提供所需的数据。
1. Defining Relationships
Realm
还支持关系。您可以声明嵌套对象以创建多对一关系。您可以使用List
创建多对多关系。声明列表时,Realm
会将那些嵌套对象与数据模型一起保存。
2. Adding an Initializer
在继续之前,打开Ingredient.swift
并在文件底部添加以下扩展名:
// MARK: Convenience init
extension Ingredient {
init(ingredientDB: IngredientDB) {
id = ingredientDB.id
title = ingredientDB.title
notes = ingredientDB.notes
bought = ingredientDB.bought
quantity = ingredientDB.quantity
}
}
此扩展创建了一个方便的初始化程序,用于将IngredientDB
映射到Ingredient
。 您稍后将在项目中使用它。
现在,您已经定义了数据模型,是时候将对象添加到数据库中了。
Adding Objects to the Database
在ViewModels
组中,打开IngredientStore.swift
。 通过在文件顶部添加以下行来导入RealmSwift
:
import RealmSwift
接下来,用以下代码替换create(title:notes:quantity :)
的正文:
objectWillChange.send()
首先,您向SwiftUI
发送信号。 由于IngredientStore
是一个ObservableObject
,因此SwiftUI
订阅objectWillChange
并通过重新加载其视图来响应信号。
接下来,将以下代码添加到方法中:
do {
let realm = try Realm()
let ingredientDB = IngredientDB()
ingredientDB.id = UUID().hashValue
ingredientDB.title = title
ingredientDB.notes = notes
ingredientDB.quantity = quantity
} catch let error {
// Handle error
print(error.localizedDescription)
}
首先,通过打开默认Realm
创建一个Realm
实例。 使用它,您可以编写,读取,更新和删除对象。 接下来,创建一个IngredientDB
对象,并根据方法的参数设置其属性值。
您可以像其他任何Swift对象一样实例化和使用Realm
数据模型。 您称这些unmanaged objects
。 这意味着数据库尚不了解它们,并且任何更改都不会持久。 将对象添加到realm
后,该对象将由Realm
管理。 这意味着Realm
将对象存储在磁盘上并跟踪其更改。
通过将以下几行代码添加到do
块的末尾,将模型添加到realm
:
try realm.write {
realm.add(ingredientDB)
}
您可以通过在Realm
上调用write
来启动写事务。 您对realm
进行的每个操作都必须在此写事务块(transaction block)
内,包括添加,删除和更新。 在事务内部,您将新的IngredientDB
实例添加到Realm
。 Realm
现在正在存储对象并跟踪其更改,使其成为managed object
。
构建并运行,然后继续创建成分! 点击New Ingredient
,为其命名,然后点击保存。
但是,等等,仍然有些不对劲。 您创建了ingredient
,但是什么也没有发生! 它仍然列出与以前相同的ingredient
。
那是因为IngredientsStore
尚未从Realm
中fetch ingredients
- 它仍在使用mock
数据。 接下来,您将解决此问题。
Fetching Objects
再次打开IngredientStore.swift
,然后找到以下内容:
var ingredients: [Ingredient] = IngredientMock.ingredientsMock
var boughtIngredients: [Ingredient] = IngredientMock.boughtIngredientsMock
使用下面替换上面代码:
private var ingredientResults: Results<IngredientDB>
private var boughtIngredientResults: Results<IngredientDB>
从Realm
获取对象时,数据库将返回Results
类型。 此类型表示从查询检索到的对象的集合。
现在,在刚添加的两行下面添加以下初始化程序:
// 1
init(realm: Realm) {
// 2
ingredientResults = realm.objects(IngredientDB.self)
.filter("bought = false")
// 3
boughtIngredientResults = realm.objects(IngredientDB.self)
.filter("bought = true")
}
以下是上述初始化程序中的操作:
- 1) 首先,您将收到一个
Realm
实例。 您将使用此实例来获取ingredients
。 - 2) 接下来,您从
realm
中fetch ingredient
,并用bought
为false
过滤它们。 - 3) 然后,您从
realm
中fetch ingredient
,并用bought
为true
过滤它们。
最后,在初始化程序之后插入以下代码:
var ingredients: [Ingredient] {
ingredientResults.map(Ingredient.init)
}
var boughtIngredients: [Ingredient] {
boughtIngredientResults.map(Ingredient.init)
}
这些属性将Realm
的Result
转换为常规数组。 示例项目的UI使用这些计算出的属性将数据库模型映射到视图。
由于IngredientStore
现在在其初始值设定项中需要一个Realm
,因此需要提供它。 打开ScenceDelegate.swift
。 在import SwiftUI
语句之后,通过插入以下内容导入RealmSwift
:
import RealmSwift
接下来,将scene(_:willConnectTo:options:)
中的代码更改为此:
if let windowScene = scene as? UIWindowScene {
do {
// 1
let realm = try Realm()
let window = UIWindow(windowScene: windowScene)
// 2
let contentView = ContentView()
.environmentObject(IngredientStore(realm: realm))
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
} catch let error {
// Handle error
fatalError("Failed to open Realm. Error: \(error.localizedDescription)")
}
}
这是您正在做的事情:
- 1) 您创建一个新的
Realm
实例。 - 2) 您可以使用
Realm
实例实例化IngredientStore
,并将其添加到ContentViews
环境中。
构建并运行。 现在创建一个ingredient
,看看魔术!
Realm
与Live Objects
一起使用。 当您向Realm
中添加ingredient
时,IngredientResults
会自动更新,而无需每次都获取它。 SwiftUI
收到信号以使用新的最新视图更新UI。 感觉像魔术,对不对? 继续创造更多ingredients
!
现在您可以成功添加ingredients
了,该构建用于更新现有ingredients
的功能的时候了。
Updating Objects
PotionsMaster
的一项关键功能是能够将成分切换到BOUGHT
列表。 现在,如果您点击购买按钮,则什么也不会发生。 要解决此问题,您可以使用Realm
更新磁盘上的成分。
1. Toggling Ingredients to BOUGHT
要将ingredient
移至BOUGHT
列表,您需要在磁盘上将bought
的属性值更新为true
。
打开IngredientStore.swift
并将toggleBought(ingredient :)
的内容替换为以下内容:
// 1
objectWillChange.send()
do {
// 2
let realm = try Realm()
try realm.write {
// 3
realm.create(
IngredientDB.self,
value: ["id": ingredient.id, "bought": !ingredient.bought],
update: .modified)
}
} catch let error {
// Handle error
print(error.localizedDescription)
}
这是这段代码中发生的事情:
- 1) 您向
SwiftUI
发送一个信号,指示该对象即将更改。 - 2) 您打开默认
realm
。 - 3) 您开始一个新的写事务并调用
create(_:value:update :)
,并传递更新的值和.modified
的case
。 这告诉Realm
使用字典中的值更新数据库。 建立字典时,您必须包含对象的id
。 如果具有该id
的对象已经存在,Realm
将使用新值更新它。 否则,Realm
将在磁盘上创建一个新对象。
构建并运行。 现在,通过点击其单元格右侧的圆形图标来购买一种ingredient
。
在更新bought
时,Realm
会将此更改同时通知IngredientResults
和buyIngredientResults
。 更新的ingredient
将移至buyedIngredientResults
。 SwiftUI
在列表List
上添加动画效果! 多么酷啊?
更新对象的另一种方法是将其属性设置为新值。 同样,您可以在写事务中执行此操作。 然后,Realm
将更新磁盘上的每个值。
2. Updating Other Properties
现在您知道如何更新对象,可以使用Realm
轻松更新其他属性。 将update(ingredientID:title:notes:quantity :)
的正文更改为以下代码:
// 1
objectWillChange.send()
do {
// 2
let realm = try Realm()
try realm.write {
// 3
realm.create(
IngredientDB.self,
value: [
"id": ingredientID,
"title": title,
"notes": notes,
"quantity": quantity
],
update: .modified)
}
} catch let error {
// Handle error
print(error.localizedDescription)
}
这是这段代码中发生的事情:
- 1) 再次,您使用
objectWillChange
发送一个信号,告诉SwiftUI
重新加载UI。 - 2) 您打开默认
realm
。 - 3) 您在写事务中调用
create(_:value:update :)
。 此调用将更新ingredient
的值。
这类似于您上面添加的用于购买ingredient
的代码。 您调用create(_:value:update :)
,并传递更新后的值。 Realm
会更新磁盘上的值,并将更改的结果通知ingredientResults
。 然后,SwiftUI
使用这些更改更新UI
。
构建并运行。 点按一种ingredient
的名称以再次打开该表格。 编辑一些字段,然后点击Update
。
现在,剩下的就是删除ingredients
了!
Deleting Objects
轻按buy
按钮可将配料移至BOUGHT
部分。 但是一旦它存在就无法摆脱它。 轻按垃圾桶图标不会执行任何操作。
要解决此问题,请打开IngredientStore.swift
。 用以下代码替换delete(ingredientID :)
的正文:
// 1
objectWillChange.send()
// 2
guard let ingredientDB = boughtIngredientResults.first(
where: { $0.id == ingredientID })
else { return }
do {
// 3
let realm = try Realm()
try realm.write {
// 4
realm.delete(ingredientDB)
}
} catch let error {
// Handle error
print(error.localizedDescription)
}
以下是删除此代码中ingredient
的方法:
- 1) 再次,您使用
objectWillChange
发送信号,请求SwiftUI
重新加载UI。 - 2) 您找到要从
buyedIngredientResults
中删除的ingredient
。 - 3) 您打开默认
realm
。 - 4) 最后,调用
delete
,传递要删除的对象。
构建并运行。 购买一种ingredient
,然后点击删除按钮将其从列表中删除。
Adding a New Property to a Realm Object
在开发过程中,数据模型会不断增长和发展。 属性类型可能会更改,并且您可能需要添加或删除属性。 使用Realm
,更改数据模型就像更改任何其他Swift类一样容易。
在本部分中,您将添加一个新属性,以按颜色识别ingredients
。
打开IngredientDB.swift
并在bought
项下添加一个新属性:
@objc dynamic var colorName = "rw-green"
接下来,在Ingredient.swift
中,添加以下属性:
var colorName = "rw-green"
您还需要更新初始化程序以设置colorName
。 在文件的初始化程序的底部添加以下行:
colorName = ingredientDB.colorName
在上面的三行代码中,您添加了一个属性,用于将颜色名称存储在Realm
上并在视图中进行映射。
就这些! 这些模型已准备好存储颜色名称。 接下来,您将更新IngredientStore.swift
将此新属性保存在数据库中。
1. Storing the New Property in Realm
打开IngredientStore.swift
,找到以下代码:
func create(title: String, notes: String, quantity: Int) {
使用下面代码替换:
func create(title: String, notes: String, quantity: Int, colorName: String) {
现在,在设置其他属性(如quantity
和 notes
)之后插入以下行:
ingredientDB.colorName = colorName
这将添加一个新参数colorName
,并将其分配给IngredientDB
。
仍然在IngredientStore.swift
中,找到以下行:
func update(ingredientID: Int, title: String, notes: String, quantity: Int) {
将它更换为:
func update(
ingredientID: Int,
title: String,
notes: String,
quantity: Int,
colorName: String
) {
最后,在update
中,找到这个代码:
realm.create(
IngredientDB.self,
value: [
"id": ingredientID,
"title": title,
"notes": notes,
"quantity": quantity
],
update: .modified)
使用下面代码进行替换:
realm.create(
IngredientDB.self,
value: [
"id": ingredientID,
"title": title,
"notes": notes,
"quantity": quantity,
"colorName": colorName
],
update: .modified)
此代码将参数colorName
添加到更新中。 调用create(_:value:update :)
时,它将其添加到value
字典中。
现在,create
和update
方法都需要一个colorName
。
但是Xcode
认识到IngredientFormFormView
在调用这些方法时没有传递colorName
,并且会产生一些错误。
要解决此问题,请打开IngredientForm.swift
。 在属性quantity
之后添加以下代码:
@Published var color = ColorOptions.rayGreen
现在找到init(_:ingredient :)
并在底部添加以下行:
color = ColorOptions(rawValue: ingredient.colorName) ?? .rayGreen
在此处,您添加了一个属性,用于在用户创建或更新ingredient
时存储颜色。
接下来,打开IngredientFormView.swift
并在saveIngredient()
中找到以下代码:
store.create(
title: form.title,
notes: form.notes,
quantity: form.quantity)
替换它使用下面:
store.create(
title: form.title,
notes: form.notes,
quantity: form.quantity,
colorName: form.color.name)
在updateIngredient()
中,您需要在调用中传递colorName
进行update
。 为此,请找到以下代码:
store.update(
ingredientID: ingredientID,
title: form.title,
notes: form.notes,
quantity: form.quantity)
替换上面,使用下面:
store.update(
ingredientID: ingredientID,
title: form.title,
notes: form.notes,
quantity: form.quantity,
colorName: form.color.name)
现在,您已经解决了IngredientFormFormView
不将colorName
传递给IngredientStore
的问题。
构建并运行。 你得到...
该应用程序崩溃了! Realm
引发迁移错误:Migration is required due to the following errors:
,但是为什么会这样呢?
Working With Migrations
启动应用程序时,Realm
会在您的代码中扫描带有Object
子类的类。找到一个模型后,它将创建一个用于将模型映射到数据库的schema
。
当您更改数据模型时,new schema
与数据库中的schema
将不匹配。如果发生这种情况,Realm
会引发错误。您必须告诉Realm
如何将旧schema
迁移到新schema
。否则,它不知道如何将旧对象映射到新schema
。
由于您向IngredientDB
添加了新属性colorName
,因此必须为其创建迁移。
注意:您可以在开发期间通过实例化
Realm.Configuration
时将true
传递给deleteRealmIfMigrationNeeded
来解决。这告诉Realm
,如果需要迁移,它应该删除其文件并创建一个新文件。
1. Creating a Migration
在Models
组中,创建一个名为RealmMigrator.swift
的文件。
现在,将此代码添加到新文件中:
import RealmSwift
enum RealmMigrator {
// 1
static private func migrationBlock(
migration: Migration,
oldSchemaVersion: UInt64
) {
// 2
if oldSchemaVersion < 1 {
// 3
migration
.enumerateObjects(ofType: IngredientDB.className()) { _, newObject in
newObject?["colorName"] = "rw-green"
}
}
}
}
细目如下:
- 1) 您定义迁移方法。 该方法接收迁移对象和
oldSchemaVersion
。 - 2) 您检查文件固定
schema
的版本,以确定要运行的迁移。 每个schema
都有一个版本号,从零开始。 在这种情况下,如果旧schema
是第一个模式(在添加新属性之前),请运行迁移。 - 3) 最后,为
Realm
中的每个旧的和新的IngredientDB
对象,为新属性分配一个默认值。
Realm
使用migrationBlock
来运行迁移并更新任何必要的属性。
在RealmMigrator
的底部,添加以下新的static
方法:
static func setDefaultConfiguration() {
// 1
let config = Realm.Configuration(
schemaVersion: 1,
migrationBlock: migrationBlock)
// 2
Realm.Configuration.defaultConfiguration = config
}
这是您在这段代码中所做的:
- 1) 您可以使用
migrationBlock
创建一个Realm.Configuration
的新实例,并将该schema
的当前版本设置为1
。 - 2) 您设置了
Realm
的新默认配置。
最后,在SceneDelegate.swift
中,在scene(_:willConnectTo:options:)
顶部调用此新方法:
RealmMigrator.setDefaultConfiguration()
Realm
使用此配置来打开默认数据库。 发生这种情况时,Realm
会检测到文件持久化schema
与新schema
之间的不匹配。 然后,它通过运行刚创建的迁移功能来迁移更改。
现在构建并再次运行。 这次崩溃不见了!
您已成功将新属性添加到IngredientDB
。 您已经做好了迁移的准备。 现在是时候更新表格,以便用户选择颜色了!
Adding a New Field to the Form
打开IngredientFormView.swift
并找到注释// TODO: Insert Picker here
。 将此代码插入注释行下方:
Picker(selection: $form.color, label: Text("Color")) {
ForEach(colorOptions, id: \.self) { option in
Text(option.title)
}
}
这会将新的picker view
添加到IngredientFormView
。 该picker
使用户可以选择颜色。
接下来,打开IngredientRow.swift
并找到注释// TODO: Insert Circle view here
。 在注释后添加以下代码:
Circle()
.fill(Color(ingredient.colorName))
.frame(width: 12, height: 12)
在这里,您要向每个ingredient
行添加一个圆形视图。 您用该ingredient
的颜色填充圆圈。
构建并运行以查看更改。 现在创建一个新ingredient
并为其选择颜色。
很好! 现在,您可以继续列出要酿造的特殊药水所需的所有成分。
在本SwiftUI Realm教程中,您学习了如何使用SwiftUI从Realm创建,更新,获取和删除对象。 除了基础知识之外,您还了解了迁移以及如何创建迁移。
要了解有关Realm
的更多信息,可以参考其official documentation。
如果您想了解更多关于SwiftUI的信息,请参阅我们的SwiftUI by Tutorials.。
后记
本篇主要讲述了基于
Realm
和SwiftUI
的数据持久化简单示例,感兴趣的给个赞或者关注~~~