Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Backup core data locally, and restore from backup - Swift

I'm struggling to find any information about creating backups of core data. My ultimate goal is to allow the user to create multiple backups, and restore from a selected backup.

I've found a sample project that allows you backup/restore locally or via iCloud in Objective-C, but nothing in swift.

Can anyone help? Or point me in the right direction. I don't even know where to start with this one.

like image 666
LateNate Avatar asked Feb 04 '18 21:02

LateNate


2 Answers

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:

  1. Create a new, second Core Data stack. Use either NSPersistentContainer or the older (but still supported) method of creating an NSPersistentStoreCoordinator.
  2. Use NSPersistentStoreCoordinator's function migratePersistentStore(_: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.
  3. 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,

  1. Display the backup list and let the user select one.
  2. Create an NSPersistentStoreCoordinator using your data model.
  3. 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.
  4. (optional) Use NSPersistentStoreCoordinator's function destroyPersistentStore(at:ofType:options:) to delete the backup.
  5. 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/

like image 194
Tom Harrington Avatar answered Nov 19 '22 21:11

Tom Harrington


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

like image 37
Yogesh Patel Avatar answered Nov 19 '22 20:11

Yogesh Patel