在本地备份核心数据,并从备份中恢复 - Swift

Backup core data locally, and restore from backup - Swift

我正在努力寻找有关创建核心数据备份的任何信息。我的最终目标是允许用户创建多个备份,并从选定的备份中恢复。

我找到了一个示例项目,它允许您 backup/restore 在本地或通过 Objective-C 中的 iCloud,但在 swift 中什么也没有。

有人可以帮忙吗?或者给我指出正确的方向。我什至不知道从哪里开始。

只是我的两分钱,以获得更有价值的想法:为了解决这个问题,我会尝试使用 CloudKit 创建不同的客户区域,以将核心数据的不同备份存储在 iCloud 中应用程序用户的私人数据库中。

我从来不需要这样做,但如果我这样做了,我就会这样做。

进行备份

随时使用以下步骤:

  1. 创建一个新的第二个核心数据堆栈。使用 NSPersistentContainer 或较旧的(但仍受支持)方法创建 NSPersistentStoreCoordinator.
  2. 使用NSPersistentStoreCoordinator的函数migratePersistentStore(_:to:options:withType:)创建备份。使用 UUID 或时间戳,使目标 URL 包含一些独特的东西。将备份放在文档文件夹中。
  3. 按日期保留备份列表。您可以将其放入 UserDefaults 或创建一个新的 属性 列表文件以保存备份信息。

第 2 步将从核心数据堆栈中删除原始存储——这就是您在第 1 步中创建第二个堆栈的原因。这样您就可以使用第二个堆栈进行备份,而不会影响您的应用程序正在使用的堆栈。

如果您正在使用 NSPersistentContainer,请使用其 persistentStoreCoordinator 属性 执行步骤 #2。

从备份恢复

这有点棘手,因为您的应用可能正在使用其持久存储,但现在您想将其替换为旧版本。 在从备份恢复之前,请确保您当前没有使用持久存储中的任何托管对象。释放您的 NSPersistentContainer。卸载任何使用托管对象的 UI。让您的应用程序进入一种状态,它所能做的就是从备份中恢复或返回使用当前数据,但它不会显示除备份列表之外的任何数据。

既然你已经做到了,

  1. 显示备份列表,让用户select一个。
  2. 使用您的数据模型创建 NSPersistentStoreCoordinator
  3. 使用replacePersistentStore(at:destinationOptions:withPersistentStoreFrom:sourceOptions:ofType:)方法将备份数据复制到正常的应用程序位置。起始位置是备份位置,目的地是应用程序通常保存其数据的位置。
  4. (可选)使用NSPersistentStoreCoordinator的功能destroyPersistentStore(at:ofType:options:)删除备份。
  5. 照常加载 NSPersistentContainer 并重新加载常规应用程序 UI。

请勿使用 FileManager 等与文件相关的直接 API。 Core Data 方法将涵盖所有与 Core Data 相关的文件,并做其他一些不错的事情,例如避免导致数据损坏和尊重文件锁定。

更新:我后来写了一篇博客post更详细地介绍了这一点,示例代码:https://atomicbird.com/blog/core-data-back-up-store/

我在 Tom 的回答和 Apple 示例的帮助下创建了以下方法 code。 这将备份核心数据文件并将其放置到您想要的路径。

Swift 5

    /// Backing up store type to a new and unique location
    /// The method is illustrated in the following code fragment, which shows how you can use migratePersistentStore to take a back up of a store and save it from one location to another.
    /// If the old store type is XML, the example also converts the store to SQLite.
    /// - Parameters:
    ///   - path: Where you want the backup to be done, please create a new unique directory with timestamp or the guid
    ///   - completion: Passes error in case of error or pass nil in case of success
     class func backUpCoreDataFiles(path : URL, completion : @escaping (_ error : String?) -> ())
        {

            // Every time new container is a must as migratePersistentStore method will loose the reference to the container on migration
            let container = NSPersistentContainer(name : "<YourDataModelName>")
            container.loadPersistentStores
                { (storeDescription, error) in
                    if let error = error
                    {
                        fatalError("Failed to load store: \(error)")
                    }
            }
            let coordinator = container.persistentStoreCoordinator
            let store = coordinator.persistentStores[0]
            do
            {
                try coordinator.migratePersistentStore(store, to : path, options : nil, withType : NSSQLiteStoreType)
                completion(nil)
            }
            catch
            {
                completion("\(Errors.coredataBackupError)\(error.localizedDescription)")
            }
        }

详情

  • Swift5.1,Xcode11.3.1

核心数据备份

 func backup(backupName: String){
        let backUpFolderUrl = FileManager.default.urls(for: .documentDirectory, in:.userDomainMask).first!
        let backupUrl = backUpFolderUrl.appendingPathComponent(backupName + ".sqlite")
        let container = NSPersistentContainer(name: "Your Project Name")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in })

        let store:NSPersistentStore
        store = container.persistentStoreCoordinator.persistentStores.last!
        do {
            try container.persistentStoreCoordinator.migratePersistentStore(store,to: backupUrl,options: nil,withType: NSSQLiteStoreType)
        } catch {
            print("Failed to migrate")
        }
    }

就是这样!

现在,

核心数据恢复

func restoreFromStore(backupName: String){

        print(DatabaseHelper.shareInstance.getAllUsers())
        let storeFolderUrl = FileManager.default.urls(for: .applicationSupportDirectory, in:.userDomainMask).first!
        let storeUrl = storeFolderUrl.appendingPathComponent("YourProjectName.sqlite")
        let backUpFolderUrl = FileManager.default.urls(for: .documentDirectory, in:.userDomainMask).first!
        let backupUrl = backUpFolderUrl.appendingPathComponent(backupName + ".sqlite")

        let container = NSPersistentContainer(name: "YourProjectName")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            let stores = container.persistentStoreCoordinator.persistentStores

            for store in stores {
                print(store)
                print(container)
            }
            do{
                try container.persistentStoreCoordinator.replacePersistentStore(at: storeUrl,destinationOptions: nil,withPersistentStoreFrom: backupUrl,sourceOptions: nil,ofType: NSSQLiteStoreType)
                print(DatabaseHelper.shareInstance.getAllUsers())
            } catch {
                print("Failed to restore")
            }

        })

    }

用法

self.backup(backupName: "first_backup")
self.restoreFromStore(backupName: "first_backup")

就是这样。希望对您有所帮助。谢谢