Delete/Reset all entries in Core Data?

Viewed 164726

Do you know of any way to delete all of the entries stored in Core Data? My schema should stay the same; I just want to reset it to blank.


Edit

I'm looking to do this programmatically so that a user can essentially hit a reset button.

33 Answers

You can still delete the file programmatically, using the NSFileManager:removeItemAtPath:: method.

NSPersistentStore *store = ...;
NSError *error;
NSURL *storeURL = store.URL;
NSPersistentStoreCoordinator *storeCoordinator = ...;
[storeCoordinator removePersistentStore:store error:&error];
[[NSFileManager defaultManager] removeItemAtPath:storeURL.path error:&error];

Then, just add the persistent store back to ensure it is recreated properly.

The programmatic way for iterating through each entity is both slower and prone to error. The use for doing it that way is if you want to delete some entities and not others. However you still need to make sure you retain referential integrity or you won't be able to persist your changes.

Just removing the store and recreating it is both fast and safe, and can certainly be done programatically at runtime.

Update for iOS5+

With the introduction of external binary storage (allowsExternalBinaryDataStorage or Store in External Record File) in iOS 5 and OS X 10.7, simply deleting files pointed by storeURLs is not enough. You'll leave the external record files behind. Since the naming scheme of these external record files is not public, I don't have a universal solution yet. – an0 May 8 '12 at 23:00

You can delete the SQLite file - but I choose to do it by purging the tables individually with a functions:

- (void) deleteAllObjects: (NSString *) entityDescription  {
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:entityDescription inManagedObjectContext:_managedObjectContext];
    [fetchRequest setEntity:entity];

    NSError *error;
    NSArray *items = [_managedObjectContext executeFetchRequest:fetchRequest error:&error];
    [fetchRequest release];


    for (NSManagedObject *managedObject in items) {
        [_managedObjectContext deleteObject:managedObject];
        DLog(@"%@ object deleted",entityDescription);
    }
    if (![_managedObjectContext save:&error]) {
        DLog(@"Error deleting %@ - error:%@",entityDescription,error);
    }

}

The reason I chose to do it table by table is that it makes me confirm as I am doing the programming that deleting the contents of the table is sensible and there is not data that I would rather keep.

Doing it this will is much slower than just deleting the file and I will change to a file delete if I this method takes too long.

[Late answer in response to a bounty asking for newer responses]

Looking over earlier answers,

  • Fetching and deleting all items, as suggested by @Grouchal and others, is still an effective and useful solution. If you have very large data stores then it might be slow, but it still works very well.
  • Simply removing the data store is, as you and @groundhog note, no longer effective. It's obsolete even if you don't use external binary storage because iOS 7 uses WAL mode for SQLite journalling. With WAL mode there may be (potentially large) journal files sitting around for any Core Data persistent store.

But there's a different, similar approach to removing the persistent store that does work. The key is to put your persistent store file in its own sub-directory that doesn't contain anything else. Don't just stick it in the documents directory (or wherever), create a new sub-directory just for the persistent store. The contents of that directory will end up being the persistent store file, the journal files, and the external binary files. If you want to nuke the entire data store, delete that directory and they'll all disappear.

You'd do something like this when setting up your persistent store:

NSURL *storeDirectoryURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"persistent-store"];
if ([[NSFileManager defaultManager] createDirectoryAtURL:storeDirectoryURL
        withIntermediateDirectories:NO
        attributes:nil
        error:nil]) {
    NSURL *storeURL = [storeDirectoryURL URLByAppendingPathComponent:@"MyApp.sqlite"];
    // continue with storeURL as usual...
}

Then when you wanted to remove the store,

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

That recursively removes both the custom sub-directory and all of the Core Data files in it.

This only works if you don't already have your persistent store in the same folder as other, important data. Like the documents directory, which probably has other useful stuff in it. If that's your situation, you could get the same effect by looking for files that you do want to keep and removing everything else. Something like:

NSString *docsDirectoryPath = [[self applicationDocumentsDirectory] path];
NSArray *docsDirectoryContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:docsDirectoryPath error:nil];
for (NSString *docsDirectoryItem in docsDirectoryContents) {
    // Look at docsDirectoryItem. If it's something you want to keep, do nothing.
    // If it's something you don't recognize, remove it.
}

This approach may be error prone. You've got to be absolutely sure that you know every file you want to keep, because otherwise you might remove important data. On the other hand, you can remove the external binary files without actually knowing the file/directory name used to store them.

iOS 10 + Swift 3 solution:

func clearCoreDataStore() {
    let delegate = UIApplication.shared.delegate as! AppDelegate
    let context = delegate.persistentContainer.viewContext

    for i in 0...delegate.persistentContainer.managedObjectModel.entities.count-1 {
        let entity = delegate.persistentContainer.managedObjectModel.entities[i]

        do {
            let query = NSFetchRequest<NSFetchRequestResult>(entityName: entity.name!)
            let deleterequest = NSBatchDeleteRequest(fetchRequest: query)
            try context.execute(deleterequest)
            try context.save()

        } catch let error as NSError {
            print("Error: \(error.localizedDescription)")
            abort()
        }
    }
}

Iterates through all of the core data entities and clears them

Swift 4/5, iOS 9+

Rebuilding the whole CoreData SQLite file will make sure all data is erased, therefore all entities are deleted. Just call deleteAndRebuild().

class CoreDataStack {
    // Change this
    static let datamodelName = "ProjectName"
    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
            if let error = error {
                fatalError(error.localizedDescription)
            }
        })
    }

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

        loadStores()
    }
}

One other method (apart from a delete batch request) I often use (based on app requirement) is to reset the persistent store. The implementation looks like this for iOS 10+ and Swift (assuming you have a CoreDataManager class):

let persistentContainer: NSPersistentContainer = {
    let container = NSPersistentContainer(name: "<Data-Model-Name>“)
    container.loadPersistentStores(completionHandler: { (storeDescription, err) in
        if let err = err {
            fatalError("loading of store failed: \(err)")
        }
    })
    return container
}()

func resetPersistentStore() {

    if let persistentStore = persistentContainer.persistentStoreCoordinator.persistentStores.last {
        let storeURL = persistentContainer.persistentStoreCoordinator.url(for: persistentStore)

        do {
            try persistentContainer.persistentStoreCoordinator.destroyPersistentStore(at: storeURL, ofType: NSSQLiteStoreType, options: nil)
        } catch {
            print("failed to destroy persistent store:", error.localizedDescription)
        }

        do {
            try persistentContainer.persistentStoreCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: nil)
        } catch {
            print("failed to re-add persistent store:", error.localizedDescription)
        }
    }

}

One advantage of this method is that it’s more straightforward especially when you have loads of data record for numerous entities in your core data. In which case a delete batch request would be memory intensive.

Swift 5.1 Solution

public static func reset() {
    let coordinator = _persistentContainer.persistentStoreCoordinator
    for store in coordinator.persistentStores where store.url != nil {
        try? coordinator.remove(store)
        try? FileManager.default.removeItem(atPath: store.url!.path)
    }
}

There seems to be two approaches:

Say you have a typical singleton for your core data stack.

import CoreData
public let core = Core.shared
public final class Core {
    static let shared = Core()
    var container: NSPersistentContainer!
    private init() {
        container = NSPersistentContainer(name: "stuff")
        //deleteSql()
        container.loadPersistentStores { storeDescription, error in
            if let error = error { print("Error loading... \(error)") }
        }
        //deleteAll()
    }
    
    func saveContext() { // typical save helper
        if container.viewContext.hasChanges {
            do { try container.viewContext.save()
            } catch { print("Error saving... \(error)") }
        }
    }
    

then ...

    func deleteSql() {
        let url = FileManager.default.urls(
           for: .applicationSupportDirectory,
           in: .userDomainMask)[0].appendingPathComponent( "stuff.sqlite" )
        
        guard FileManager.default.fileExists(atPath: url.path) else {
            print("nothing to delete!")
            return
        }
        
        do {
            try container.persistentStoreCoordinator.destroyPersistentStore(
                at: url, ofType: "sqlite", options: nil)
            print("totally scorched the sql file. you DO now have to LOAD again")
        }
        catch {
            print("there was no sql file there!")
        }
    }
    
    func deleteAll() { // courtesy @Politta
        for e in container.persistentStoreCoordinator.managedObjectModel.entities {
            let r = NSBatchDeleteRequest(
              fetchRequest: NSFetchRequest(entityName: e.name ?? ""))
            let _ = try? container.viewContext.execute(r)
        }
        saveContext()
        print("conventionally deleted everything from within core data. carry on")
    }
}

.

1. scorch the sql file

Courtesy the excellent @J.Doe answer. You completely destroy the sql file.

You must to do this

  • before loading core data, ie, after creating the container, but
  • before actually loading the stores.)

(Notice the example line of code "//deleteSql()" is just before initialization.)

2. delete everything one by one within core data

Courtesy the excellent @Politta answer. You could do this at any time once core data is up and running.

(Notice the example line of code "//deleteAll()" is after initialization.)

Approach one is probably more useful during development. Approach two is probably more useful in production (in the relatively unusual case that for some reason you need to wipe everything).

Delete the persistent store file and setup a new persistent store coordinator?

Delete sqlite from your fileURLPath and then build.

you're all making this seem complicated. You can just send your NSManagedObjectContext the reset method

Related