Reputation: 12499
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
Upvotes: 11
Views: 10022
Reputation: 826
OK So I needed to do this in my app, ended up writing the following function.
Note: Call this BEFORE you load your CoreData store (I added it to the init() of the App (it's a swiftUI app), which seemed to work.
Also note that the above answers referring to
destroyPersistentStore(at: url, ofType: storeType, options: nil)
did not work for me because it seemed to not delete the .sqlite-wal file or the sqlite.shm file.
class CoreDataContext {
static let datamodelName = "cedarca"
static let storeType = "sqlite"
static let persistentContainer = NSPersistentContainer(name: datamodelName)
class func deleteAndRebuild() {
let url = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0].appendingPathComponent("\(datamodelName).\(storeType)")
let url1 = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0].appendingPathComponent("\(datamodelName).sqlite-shm")
let url2 = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0].appendingPathComponent("\(datamodelName).sqlite-wal")
if FileManager.default.fileExists(atPath: url.path) {
do {
try FileManager.default.removeItem(at: url)
try FileManager.default.removeItem(at: url1)
try FileManager.default.removeItem(at: url2)
} catch {
print(error)
}
}
}
}
Upvotes: 0
Reputation: 13013
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 :)
Upvotes: 13
Reputation: 137
I think destroyPersistentStoreAtURL
method of NSPersistentStoreCoordinator
is what you want.
It will remove the data store and journal files and all other things that need to be removed.
Look at Apple documentation.
Upvotes: 7
Reputation: 3711
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.
Upvotes: 11