美文网首页
数据持久化方案解析(十五) —— 基于Realm和SwiftUI

数据持久化方案解析(十五) —— 基于Realm和SwiftUI

作者: 刀客传奇 | 来源:发表于2020-10-19 11:31 被阅读0次

    版本记录

    版本号 时间
    V1.0 2020.10.19 星期一

    前言

    数据的持久化存储是移动端不可避免的一个问题,很多时候的业务逻辑都需要我们进行本地化存储解决和完成,我们可以采用很多持久化存储方案,比如说plist文件(属性列表)、preference(偏好设置)、NSKeyedArchiver(归档)、SQLite 3CoreData,这里基本上我们都用过。这几种方案各有优缺点,其中,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教程中,您将通过构建购物清单应用程序来学习如何将RealmSwiftUI一起用作数据持久性解决方案。内容来自翻译

    下面看下写作环境:

    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不同,RealmNoSQL对象数据库。像任何其他NoSQL数据库一样,它也有优点和缺点。但是Realm是使多平台团队保持同步的绝佳选择。

    1. Understanding the Realm Database

    在开始设置Realm之前,您需要了解一些工作原理。

    Realm使用文件来保存和管理数据库。应用中的每个Realm数据库都称为一个realm。您的应用程序可能有多个realm,每个realm都处理不同的对象域(domain of objects)。这有助于使您的数据库井井有条,简洁明了。而且,由于跨平台可用,因此您可以在iOSAndroid等平台之间共享预加载的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 ManagerCocoaPods。 本教程使用Swift Package Manager,但请随意使用您更喜欢的工具。

    注意:如果您不熟悉Swift Package Manager,或者想了解更多信息,请查看我们的Swift Package Manager for iOS tutorial教程。

    要设置您的依赖关系,请选择File ▸ Swift Packages ▸ Add Package Dependency…。 复制以下内容并粘贴到组合的搜索/输入框中:

    https://github.com/realm/realm-cocoa.git

    这是包含Realm软件包的GitHub存储库的位置。 点击Next

    接下来,Xcode要求您为此程序包定义一些选项。 只需保留Up To Next Major的默认值即可使用Realm的最新版本。 (因此,在撰写本文时,此值从5.4.0(含)到6.0.0(不含)。)单击Next

    最后,选择应添加到项目中的包装产品和target。 选择RealmRealmSwift,然后单击FinishXcode从存储库下载代码,并将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) 首先,定义您的类,继承自ObjectRealm是所有数据模型的基类。您将使用该类将成分保存在Realm中。
    • 2) 在这里,您定义要Realm存储的每个Ingredient属性。
    • 3) 最后,您重写primaryKey()告诉Realm哪个属性是模型的主键。Realm使用主键来强制唯一性。主键还提供了一种获取和更新数据的有效方法。

    就这些!

    您将Realm数据模型定义为常规Swift类。 Realm使用这些类在磁盘上写入数据,但是对Object子类有一些限制。由于跨平台性质,Realm仅支持有限的一组与平台无关的属性类型。其中一些属性是:

    • Bool
    • Int
    • Double
    • Float
    • String
    • Date
    • Data

    Optional属性具有特殊限制。您可以将String,DateData声明为可选。其余部分,使用包装器类RealmOptional;否则,它们必须具有值。

    每个属性都有@objc dynamic关键字。这使得它们可以在运行时通过Dynamic Dispatch访问。 Realm使用此SwiftObjective-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实例添加到RealmRealm现在正在存储对象并跟踪其更改,使其成为managed object

    构建并运行,然后继续创建成分! 点击New Ingredient,为其命名,然后点击保存。

    但是,等等,仍然有些不对劲。 您创建了ingredient,但是什么也没有发生! 它仍然列出与以前相同的ingredient

    那是因为IngredientsStore尚未从Realmfetch 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) 接下来,您从realmfetch ingredient,并用boughtfalse过滤它们。
    • 3) 然后,您从realmfetch ingredient,并用boughttrue过滤它们。

    最后,在初始化程序之后插入以下代码:

    var ingredients: [Ingredient] {
      ingredientResults.map(Ingredient.init)
    }
    
    var boughtIngredients: [Ingredient] {
      boughtIngredientResults.map(Ingredient.init)
    }
    

    这些属性将RealmResult转换为常规数组。 示例项目的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,看看魔术!

    RealmLive 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 :),并传递更新的值和.modifiedcase。 这告诉Realm使用字典中的值更新数据库。 建立字典时,您必须包含对象的id。 如果具有该id的对象已经存在,Realm将使用新值更新它。 否则,Realm将在磁盘上创建一个新对象。

    构建并运行。 现在,通过点击其单元格右侧的圆形图标来购买一种ingredient

    在更新bought时,Realm会将此更改同时通知IngredientResultsbuyIngredientResults。 更新的ingredient将移至buyedIngredientResultsSwiftUI在列表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) {
    

    现在,在设置其他属性(如quantitynotes)之后插入以下行:

    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字典中。

    现在,createupdate方法都需要一个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.。

    后记

    本篇主要讲述了基于RealmSwiftUI的数据持久化简单示例,感兴趣的给个赞或者关注~~~

    相关文章

      网友评论

          本文标题:数据持久化方案解析(十五) —— 基于Realm和SwiftUI

          本文链接:https://www.haomeiwen.com/subject/rtzzpktx.html