Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core Data: how to just delete and rebuild the data store?

I'm using Core Data in an iOS 7+ app that does not need to save user's data, all data the app needs is requested to services and it can be recovered at any time. So, if I change my data model in a next app update, I have no problem in deleting all the previous data and requesting it all again. But I don't know how to simply replace the previous data model with the new one, without performing a migration since it looks that I don't need to do that...

Thanks in advance

like image 505
AppsDev Avatar asked Mar 13 '15 07:03

AppsDev


People also ask

How do I clear my Core Data?

One approach to delete everything and reset Core Data is to destroy the persistent store. Deleting and re-creating the persistent store will delete all objects in Core Data.

Where is Core Data saved?

The persistent store should be located in the AppData > Library > Application Support directory.

What is NSPersistentStoreCoordinator?

A persistent store coordinator is an instance of NSPersistentStoreCoordinator . It has a reference to a managed object model that describes the entities in the store or stores it manages. The coordinator is the central object in a Core Data stack.

Do I need Core Data?

The next time you need to store data, you should have a better idea of your options. Core Data is unnecessary for random pieces of unrelated data, but it's a perfect fit for a large, relational data set. The defaults system is ideal for small, random pieces of unrelated data, such as settings or the user's preferences.


2 Answers

Case 1: You're using a SQLite store

This applies if your store type is NSSQLiteStoreType. Even if you plan to delete your data from time to time, it's not a bad idea to stick to SQLite, as it gives you the flexibility to keep your cached data on disk as long as you wish, and only delete it when you change your model and you don't want to apply any migrations.

Quick solution? Delete your NSPersistentStoreCoordinator's store at startup, when you're initializing Core Data. For instance, if you're using the default SQLite store, provided by Apple's boilerplate code:

NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"cd.sqlite"]

you can simply delete the file:

[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil];

then use storeURL to add a new persistent store as usual.
Your NSPersistentStoreCoordinator won't complain if you have a new model and you won't need any migrations, but your data will be lost, of course.

It goes without saying that you can decide to use this solution when you detect a change in the data model, leaving the store alone if there's no change, so that you can preserve your cached data as long as necessary.

UPDATE:

As suggested by TomHerrington in the comments, to be sure you've removed the old store completely, you should also delete journal files, which might come back and haunt you in the future, if you don't take care of them.
If your store file is named cd.sqlite, like in the example, the additional files to be removed are cd.sqlite-shm and cd.sqlite-wal.

WAL journaling mode for Core Data was introduced as the default in iOS 7 and OSX Mavericks, as reported by Apple in QA1809.

Case 2: Use an in-memory store

As suggested, you could switch to an in-memory store, using NSInMemoryStoreType instead of NSSQLiteStoreType. Erasing the store is much easier in this case: all your data resides in memory, all of it will disappear when your app stops running, nothing will remain on disk for you to clean. Next time, you could potentially load a completely different model, without any migrations, as there's no data to migrate.
However, this solution, implemented as it is, wouldn't let you cache data between sessions, which looks like something you'd like to do between app updates (i.e., the store must be erased only when the app is updated and the model changes, while it could be useful to keep it on disk otherwise).

Note:

Both approaches are viable, with their pros and cons, and I'm sure there could be other strategies as well. In the end, you should have all the elements to decide what the best approach would be in your specific case.

like image 50
Para Avatar answered Oct 13 '22 00:10

Para


Working Swift solution if you target iOS 9 or higher

The shared CoreData manager:

class CoreDataContext {
    static let datamodelName = "CoreDataTests"
    static let storeType = "sqlite"

    static let persistentContainer = NSPersistentContainer(name: datamodelName)
    private static let url: URL = {
        let url = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0].appendingPathComponent("\(datamodelName).\(storeType)")

        assert(FileManager.default.fileExists(atPath: url.path))

        return url
    }()

    static func loadStores() {
        persistentContainer.loadPersistentStores(completionHandler: { (nsPersistentStoreDescription, error) in
            guard let error = error else {
                return
            }
            fatalError(error.localizedDescription)
        })
    }

    static func deleteAndRebuild() {
        try! persistentContainer.persistentStoreCoordinator.destroyPersistentStore(at: url, ofType: storeType, options: nil)

        loadStores()
    }
}

call loadStores only once in the appDelegate and deleteAndRebuild when you want to delete and rebuild the database :)

like image 23
J. Doe Avatar answered Oct 12 '22 22:10

J. Doe