在本地备份核心数据,并从备份中恢复 - Swift
Backup core data locally, and restore from backup - Swift
我正在努力寻找有关创建核心数据备份的任何信息。我的最终目标是允许用户创建多个备份,并从选定的备份中恢复。
我找到了一个示例项目,它允许您 backup/restore 在本地或通过 Objective-C 中的 iCloud,但在 swift 中什么也没有。
有人可以帮忙吗?或者给我指出正确的方向。我什至不知道从哪里开始。
只是我的两分钱,以获得更有价值的想法:为了解决这个问题,我会尝试使用 CloudKit 创建不同的客户区域,以将核心数据的不同备份存储在 iCloud 中应用程序用户的私人数据库中。
我从来不需要这样做,但如果我这样做了,我就会这样做。
进行备份
随时使用以下步骤:
- 创建一个新的第二个核心数据堆栈。使用
NSPersistentContainer
或较旧的(但仍受支持)方法创建 NSPersistentStoreCoordinator
.
- 使用
NSPersistentStoreCoordinator
的函数migratePersistentStore(_:to:options:withType:)
创建备份。使用 UUID 或时间戳,使目标 URL 包含一些独特的东西。将备份放在文档文件夹中。
- 按日期保留备份列表。您可以将其放入
UserDefaults
或创建一个新的 属性 列表文件以保存备份信息。
第 2 步将从核心数据堆栈中删除原始存储——这就是您在第 1 步中创建第二个堆栈的原因。这样您就可以使用第二个堆栈进行备份,而不会影响您的应用程序正在使用的堆栈。
如果您正在使用 NSPersistentContainer
,请使用其 persistentStoreCoordinator
属性 执行步骤 #2。
从备份恢复
这有点棘手,因为您的应用可能正在使用其持久存储,但现在您想将其替换为旧版本。 在从备份恢复之前,请确保您当前没有使用持久存储中的任何托管对象。释放您的 NSPersistentContainer
。卸载任何使用托管对象的 UI。让您的应用程序进入一种状态,它所能做的就是从备份中恢复或返回使用当前数据,但它不会显示除备份列表之外的任何数据。
既然你已经做到了,
- 显示备份列表,让用户select一个。
- 使用您的数据模型创建
NSPersistentStoreCoordinator
。
- 使用
replacePersistentStore(at:destinationOptions:withPersistentStoreFrom:sourceOptions:ofType:)
方法将备份数据复制到正常的应用程序位置。起始位置是备份位置,目的地是应用程序通常保存其数据的位置。
- (可选)使用
NSPersistentStoreCoordinator
的功能destroyPersistentStore(at:ofType:options:)
删除备份。
- 照常加载
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")
就是这样。希望对您有所帮助。谢谢
我正在努力寻找有关创建核心数据备份的任何信息。我的最终目标是允许用户创建多个备份,并从选定的备份中恢复。
我找到了一个示例项目,它允许您 backup/restore 在本地或通过 Objective-C 中的 iCloud,但在 swift 中什么也没有。
有人可以帮忙吗?或者给我指出正确的方向。我什至不知道从哪里开始。
只是我的两分钱,以获得更有价值的想法:为了解决这个问题,我会尝试使用 CloudKit 创建不同的客户区域,以将核心数据的不同备份存储在 iCloud 中应用程序用户的私人数据库中。
我从来不需要这样做,但如果我这样做了,我就会这样做。
进行备份
随时使用以下步骤:
- 创建一个新的第二个核心数据堆栈。使用
NSPersistentContainer
或较旧的(但仍受支持)方法创建NSPersistentStoreCoordinator
. - 使用
NSPersistentStoreCoordinator
的函数migratePersistentStore(_:to:options:withType:)
创建备份。使用 UUID 或时间戳,使目标 URL 包含一些独特的东西。将备份放在文档文件夹中。 - 按日期保留备份列表。您可以将其放入
UserDefaults
或创建一个新的 属性 列表文件以保存备份信息。
第 2 步将从核心数据堆栈中删除原始存储——这就是您在第 1 步中创建第二个堆栈的原因。这样您就可以使用第二个堆栈进行备份,而不会影响您的应用程序正在使用的堆栈。
如果您正在使用 NSPersistentContainer
,请使用其 persistentStoreCoordinator
属性 执行步骤 #2。
从备份恢复
这有点棘手,因为您的应用可能正在使用其持久存储,但现在您想将其替换为旧版本。 在从备份恢复之前,请确保您当前没有使用持久存储中的任何托管对象。释放您的 NSPersistentContainer
。卸载任何使用托管对象的 UI。让您的应用程序进入一种状态,它所能做的就是从备份中恢复或返回使用当前数据,但它不会显示除备份列表之外的任何数据。
既然你已经做到了,
- 显示备份列表,让用户select一个。
- 使用您的数据模型创建
NSPersistentStoreCoordinator
。 - 使用
replacePersistentStore(at:destinationOptions:withPersistentStoreFrom:sourceOptions:ofType:)
方法将备份数据复制到正常的应用程序位置。起始位置是备份位置,目的地是应用程序通常保存其数据的位置。 - (可选)使用
NSPersistentStoreCoordinator
的功能destroyPersistentStore(at:ofType:options:)
删除备份。 - 照常加载
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")
就是这样。希望对您有所帮助。谢谢