Backup core data locally, and restore from backup - Swift
I've never needed to do this but if I did this is what I'd do.
To make backups
At any time, use the following steps:
- Create a new, second Core Data stack. Use either
NSPersistentContainer
or the older (but still supported) method of creating anNSPersistentStoreCoordinator
. - Use
NSPersistentStoreCoordinator
's functionmigratePersistentStore(_:to:options:withType:)
to create the backup. Make the destination URL include something unique, using a UUID or a timestamp. Put the backups in the documents folder. - Keep a list of backups by date. You could put this in
UserDefaults
or create a new property list file to save backup info.
Step #2 will remove the original store from the Core Data stack-- which is why you create a second stack in step #1. This way you can use the second stack to make the backup without affecting the one your app is using.
If you're using NSPersistentContainer
, use its persistentStoreCoordinator
property to carry out step #2.
To restore from backups
This is a little bit tricky because your app may be using its persistent store, but now you want to replace that with an older version. Before restoring from a backup, make sure you're not currently using any managed objects from the persistent store. Deallocate your NSPersistentContainer
. Unload any UI that makes use of managed objects. Get your app into a state where all it can do is either restore from a backup or go back to using the current data, but where it's not showing any data except the backup list.
Now that you've done that,
- Display the backup list and let the user select one.
- Create an
NSPersistentStoreCoordinator
using your data model. - Use the
replacePersistentStore(at:destinationOptions:withPersistentStoreFrom:sourceOptions:ofType:)
method to copy the backup data to the normal app location. The starting location is the backup location, the destination is where the app normally saves its data. - (optional) Use
NSPersistentStoreCoordinator
's functiondestroyPersistentStore(at:ofType:options:)
to delete the backup. - Load an
NSPersistentContainer
as usual and reload the regular app UI.
Don't use direct file-related APIs like FileManager
for any of this. The Core Data methods will cover all of the Core Data-related files and do other nice things like avoid causing data corruption and respecting file locks.
Update: I later wrote a blog post that covers this in more detail, with sample code: https://atomicbird.com/blog/core-data-back-up-store/
Details
- Swift 5.1, Xcode 11.3.1
Core-Data Backup
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")
}
}
That's it!
Now,
Core-Data Restore
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")
}
})
}
Usage
self.backup(backupName: "first_backup")
self.restoreFromStore(backupName: "first_backup")
That's it.. Hope this helpful. Thank You