当前位置:首页 > 科技  > 软件

Swift 定制 Core Data 迁移

来源: 责编: 时间:2024-05-17 17:48:49 248观看
导读前言随着应用程序和用户群的增长,你需要添加新功能,删除其他功能,并改变应用程序的工作方式。这是软件开发生命周期的自然结果,我们应该接受。随着应用程序的发展,你的数据模型也会发生变化。你需要更改数据结构的方式,以适

XCZ28资讯网——每日最新资讯28at.com

前言

随着应用程序和用户群的增长,你需要添加新功能,删除其他功能,并改变应用程序的工作方式。这是软件开发生命周期的自然结果,我们应该接受。XCZ28资讯网——每日最新资讯28at.com

随着应用程序的发展,你的数据模型也会发生变化。你需要更改数据结构的方式,以适应新功能,同时确保用户不会在不同版本之间丢失任何数据。如果你使用 Core Data 在应用程序中持久化信息,那么 Core Data 迁移就会发挥作用。XCZ28资讯网——每日最新资讯28at.com

什么是 Core Data 迁移?

Core Data 迁移是将数据模型从一个版本更新到另一个版本的过程,因为数据的形状发生了变化(例如,添加或删除新属性)。XCZ28资讯网——每日最新资讯28at.com

在大多数情况下,Core Data 将自动处理迁移过程。但是,有些情况下,你需要通过提供一个映射模型来自定义迁移过程,告诉 Core Data 究竟如何从源模型迁移到目标模型中的每个属性和实体。XCZ28资讯网——每日最新资讯28at.com

甚至有些情况下,映射模型是不够的,你需要编写自定义迁移策略来处理特定情况。这是本文要重点讨论的情况。XCZ28资讯网——每日最新资讯28at.com

示例

让我们考虑一个应用程序,在 Core Data 栈中存储表示音乐曲目的对象。模型非常简单,只包含一个实体:Track,Track.swift 代码如下:XCZ28资讯网——每日最新资讯28at.com

Copy codeTrack.swiftimport Foundationimport CoreData@objc(Track)public class Track: NSManagedObject, Identifiable {    @nonobjc public class func fetchRequest() -> NSFetchRequest<Track> {        return NSFetchRequest<Track>(entityName: "Track")    }    @NSManaged public var imageURL: String?    @NSManaged public var json: String?    @NSManaged public var lastPlayedAt: Date?    @NSManaged public var title: String?    @NSManaged public var artistName: String?}

上面的 Track 实体有五个属性:XCZ28资讯网——每日最新资讯28at.com

  • imageURL:表示曲目封面图像的 URL 的字符串。
  • json:表示来自服务器的原始 JSON 数据响应的字符串。
  • lastPlayedAt:表示上次播放曲目的日期。
  • title:表示曲目的标题的字符串。
  • artistName:表示艺术家的名称的字符串。

Core Data 栈不会与 iCloud 同步,并具有以下设置,CoreDataStack.swift 文件代码如下:XCZ28资讯网——每日最新资讯28at.com

Copy codeCoreDataStack.swiftimport CoreDatastruct PersistenceController {    static let shared = PersistenceController()    let container: NSPersistentContainer    init(inMemory: Bool = false) {        container = NSPersistentContainer(name: "CustomMigration")        if inMemory {            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")        }        container.viewContext.automaticallyMergesChangesFromParent = true        if let description = container.persistentStoreDescriptions.first {            description.shouldMigrateStoreAutomatically = true            description.shouldInferMappingModelAutomatically = false        }        container.loadPersistentStores(completionHandler: { (storeDescription, error) in            if let error = error as NSError? {                fatalError("Unresolved error /(error), /(error.userInfo)")            }        })    }}

如果你仔细观察上面的示例,你会注意到我们告诉 Core Data 自动迁移存储,因为我们不想做渐进式迁移,这种迁移速度慢得多且更复杂,并且我们还告诉 Core Data 不要自动推断映射模型,这意味着我们将不得不为每个迁移提供一个映射模型文件,并且可以允许我们自定义这个过程。XCZ28资讯网——每日最新资讯28at.com

持久化了一首歌曲后,使用 Core Data Lab 检查数据库,我们可以看到属性被相应保存:XCZ28资讯网——每日最新资讯28at.com

XCZ28资讯网——每日最新资讯28at.com

更新模型

当前版本的模型存在一些可扩展性问题:XCZ28资讯网——每日最新资讯28at.com

  • 模型仅允许每个曲目有一个艺术家,而实际上,一个曲目可以有多个艺术家。
  • 模型存储一个表示曲目数据的原始 JSON 字符串,这不太高效,当应用程序需要解析 JSON 字符串以显示曲目数据以获取艺术家列表时,可能会导致性能问题。

为了解决这些问题,让我们删除 artistName 和 json 属性,采用一个新的 Artist 实体,该实体将与 Track 实体建立一对多的关系。XCZ28资讯网——每日最新资讯28at.com

Artist 实体将具有一个表示艺术家名称的 name 属性,以及 id 和 imageURL 属性,我们将从原始 JSON 字符串中获取它们。XCZ28资讯网——每日最新资讯28at.com

创建一个新的模型版本

首先,让我们通过选择 .xcdatamodeld 文件,然后从菜单栏中选择 Editor > Add Model Version... 来创建一个新的模型版本。XCZ28资讯网——每日最新资讯28at.com

XCZ28资讯网——每日最新资讯28at.com

给它起一个名称,并以第一个模型版本为基础:XCZ28资讯网——每日最新资讯28at.com

XCZ28资讯网——每日最新资讯28at.com

现在,让我们创建 Artist 实体并添加所有字段:XCZ28资讯网——每日最新资讯28at.com

XCZ28资讯网——每日最新资讯28at.com

也让我们为新的 Artist 实体创建 NSManagedObject 子类,Artist.swift 代码如下:XCZ28资讯网——每日最新资讯28at.com

Copy codeimport Foundationimport CoreData@objc(Artist)public class Artist: NSManagedObject, Identifiable {    @nonobjc public class func fetchRequest() -> NSFetchRequest<Artist> {        return NSFetchRequest<Artist>(entityName: "Artist")    }    @NSManaged public var name: String?    @NSManaged public var id: String?    @NSManaged public var imageURL: String?    @NSManaged public var tracks: NSSet?    @objc(addTracksObject:)    @NSManaged public func addToTracks(_ value: Track)    @objc(removeTracksObject:)    @NSManaged public func removeFromTracks(_ value: Track)    @objc(addTracks:)    @NSManaged public func addToTracks(_ values: NSSet)    @objc(removeTracks:)    @NSManaged public func removeFromTracks(_ values: NSSet)}

正如你在上面的示例中看到的那样,我们将向 Track 实体添加一个对多的 artists 关系,还将向 Artist 实体添加一个对多的 tracks 关系。XCZ28资讯网——每日最新资讯28at.com

现在,让我们为 Track 实体添加缺失的关系,并删除 artistName 和 json 属性:XCZ28资讯网——每日最新资讯28at.com

XCZ28资讯网——每日最新资讯28at.com

并更新 NSManagedObject 子类以反映更改,Track.swift 文件代码如下:XCZ28资讯网——每日最新资讯28at.com

import Foundationimport CoreData@objc(Track)public class Track: NSManagedObject, Identifiable {    @nonobjc public class func fetchRequest() -> NSFetchRequest<Track> {        return NSFetchRequest<Track>(entityName: "Track")    }    @NSManaged public var imageURL: String?    @NSManaged public var lastPlayedAt: Date?    @NSManaged public var title: String?    @NSManaged public var artists: NSSet?    @objc(addArtistsObject:)    @NSManaged public func addToArtists(_ value: Artist)    @objc(removeArtistsObject:)    @NSManaged public func removeFromArtists(_ value: Artist)    @objc(addArtists:)    @NSManaged public func addToArtists(_ values: NSSet)    @objc(removeArtists:)    @NSManaged public func removeFromArtists(_ values: NSSet)}

最后但并非最不重要的,让我们将新的模型设置为 .xcdatamodeld 文件的当前模型:XCZ28资讯网——每日最新资讯28at.com

XCZ28资讯网——每日最新资讯28at.com

创建映射模型

由于我们告诉 Core Data 不要自动推断映射模型,所以我们将不得不创建一个映射模型文件来在两个版本之间建立桥梁。XCZ28资讯网——每日最新资讯28at.com

从菜单栏中选择 File > New > File...,然后选择 Mapping Model。XCZ28资讯网——每日最新资讯28at.com

XCZ28资讯网——每日最新资讯28at.com

然后,选择源模型:XCZ28资讯网——每日最新资讯28at.com

XCZ28资讯网——每日最新资讯28at.com

最后,选择目标模型:XCZ28资讯网——每日最新资讯28at.com

XCZ28资讯网——每日最新资讯28at.com

编写自定义迁移策略

默认情况下,Core Data 将尽力映射属性,并且大部分工作都将由它自动完成(包括已删除的属性)。XCZ28资讯网——每日最新资讯28at.com

然而,由于我们创建了一个新的实体,并且我们希望保留现有数据,因此我们需要告诉 Core Data 如何迁移。XCZ28资讯网——每日最新资讯28at.com

我们将创建一个新的类,该类继承自 NSEntityMigrationPolicy,并在旧的 Track 实体上创建并链接一个新的关系到 Artist 实体,V2MigrationPolicy.swift 文件代码如下:XCZ28资讯网——每日最新资讯28at.com

Copy codeimport CoreDatastruct Song: Decodable {    let artists: [Artist]    struct Artist: Decodable {        let id: String        let name: String        let imageURL: String    }}class V2MigrationPolicy: NSEntityMigrationPolicy {    private let decoder = JSONDecoder()    override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {        // 1        let sourceKeys = sInstance.entity.attributesByName.keys        let sourceValues = sInstance.dictionaryWithValues(forKeys: sourceKeys.map { $0 as String })        // 2        let destinationInstance = NSEntityDescription.insertNewObject(forEntityName: mapping.destinationEntityName!, into: manager.destinationContext)        let destinationKeys = destinationInstance.entity.attributesByName.keys.map { $0 as String }        // 3        for key in destinationKeys {            if let value = sourceValues[key] {                destinationInstance.setValue(value, forKey: key)            }        }        if let jsonString = sInstance.value(forKey: "json") as? String {            // 3            let jsonData = Data(jsonString.utf8)            let object = try? decoder.decode(Song.self, from: jsonData)            // 4            let artists: [NSManagedObject] = object?.artists.map { jsonArtist in                // 5                let request = Artist.fetchRequest()                request.fetchLimit = 1                request.predicate = NSPredicate(format: "name == %@", jsonArtist.name)                // Do not add duplicates to the list...                if let matchedArtists = try? manager.destinationContext.fetch(request), let matchedArtist = matchedArtists.first {                    return matchedArtist                }                // 6                let artist = NSEntityDescription.insertNewObject(forEntityName: "Artist", into: manager.destinationContext)                artist.setValue(jsonArtist.name, forKey: "name")                artist.setValue(jsonArtist.imageURL, forKey: "imageURL")                artist.setValue(jsonArtist.id, forKey: "id")                return artist            } ?? []            // 7            destinationInstance.setValue(Set<NSManagedObject>(artists), forKey: "artists")        }        // 8        manager.associate(sourceInstance: sInstance, withDestinationInstance: destinationInstance, for: mapping)    }}

让我们逐步解释上面的代码:XCZ28资讯网——每日最新资讯28at.com

  • 获取源实体的属性名称和值。
  • 创建与源实体相同类型的全新目标实体。
  • 将源实体的属性值复制到目标实体。
  • 如果源实体具有 json 属性,则将其解析为 Song 对象。
  • 为避免重复项,请检查艺术家是否已经存在于目标上下文中。
  • 如果艺术家不存在,则创建一个新的 Artist 实体,将其插入到上下文中,并设置其属性。
  • 设置目标实体上的新艺术家关系。
  • 将源和目标实例关联起来。

最后,让我们将此自定义策略添加到映射模型中:XCZ28资讯网——每日最新资讯28at.com

XCZ28资讯网——每日最新资讯28at.com

现在,如果我们再次运行应用程序并使用 Core Data Lab 检查数据库,我们可以看到一个新的实体已经填充了正确的数据。XCZ28资讯网——每日最新资讯28at.com

总结

文章介绍了在应用程序发展过程中,数据模型可能需要进行更改的情况下,如何使用 Core Data 迁移来保持数据的一致性和完整性。首先,它解释了什么是 Core Data 迁移,以及为什么需要进行迁移。接着,通过一个示例应用程序,详细介绍了如何更新数据模型,添加新实体和关系,以解决现有模型的可扩展性问题。然后,文章介绍了如何创建映射模型来定义不同模型版本之间的映射关系,并演示了如何编写自定义迁移策略来处理特定情况,例如将旧模型数据迁移到新模型的新关系中。最后,通过将自定义迁移策略添加到映射模型中,完成了整个迁移过程。XCZ28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-88935-0.htmlSwift 定制 Core Data 迁移

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 谁说PHP不能异步和并行运行?

下一篇: 如何使用 SwiftUI 构建 visionOS 应用

标签:
  • 热门焦点
  • 印度登月最关键一步!月船三号今晚进入环月轨道

    8月5日消息,据印度官方消息,月船三号将于北京时间今晚21时30分左右开始近月制动进入环月轨道。这是该探测器能够成功的最关键步骤之一,如果成功将开始围
  • 2023 年的 Node.js 生态系统

    随着技术的不断演进和创新,Node.js 在 2023 年达到了一个新的高度。Node.js 拥有一个庞大的生态系统,可以帮助开发人员更快地实现复杂的应用。本文就来看看 Node.js 最新的生
  • 得物效率前端微应用推进过程与思考

    一、背景效率工程随着业务的发展,组织规模的扩大,越来越多的企业开始意识到协作效率对于企业团队的重要性,甚至是决定其在某个行业竞争中突围的关键,是企业长久生存的根本。得物
  • Flowable工作流引擎的科普与实践

    一.引言当我们在日常工作和业务中需要进行各种审批流程时,可能会面临一系列技术和业务上的挑战。手动处理这些审批流程可能会导致开发成本的增加以及业务复杂度的上升。在这
  • 在线图片编辑器,支持PSD解析、AI抠图等

    自从我上次分享一个人开发仿造稿定设计的图片编辑器到现在,不知不觉已过去一年时间了,期间我经历了裁员失业、面试找工作碰壁,寒冬下一直没有很好地履行计划.....这些就放在日
  • 使用LLM插件从命令行访问Llama 2

    最近的一个大新闻是Meta AI推出了新的开源授权的大型语言模型Llama 2。这是一项非常重要的进展:Llama 2可免费用于研究和商业用途。(几小时前,swyy发现它已从LLaMA 2更名为Lla
  • iQOO Neo8系列或定档5月23日:首发天玑9200+ 安卓跑分王者

    去年10月,iQOO推出了iQOO Neo7系列机型,不仅搭载了天玑9000+,而且是同价位唯一一款天玑9000+直屏旗舰,一经上市便受到了用户的广泛关注。在时隔半年后,
  • 回归OPPO两年,一加赢了销量,输了品牌

    成为OPPO旗下主打性能的先锋品牌后,一加屡创佳绩。今年618期间,一加手机全渠道销量同比增长362%,凭借一加 11、一加 Ace 2、一加 Ace 2V三款爆品,一加
  • Meta盲目扩张致超万人被裁,重金押注元宇宙而前景未明

    图片来源:图虫创意日前,Meta创始人兼CEO 马克&middot;扎克伯发布公开信,宣布Meta计划裁员超11000人,占其员工总数13%。他公开承认了自己的预判失误:&ldquo;不仅
Top