web-dev-qa-db-fra.com

Données de base: le moyen le plus rapide de supprimer toutes les instances d'une entité

J'utilise Core Data pour conserver localement les résultats d'un appel de services Web. Le service Web renvoie le modèle d'objet complet pour, disons, "Voitures" - pourrait en contenir environ 2 000 (et je ne peux pas faire en sorte que le service Web ne renvoie rien de moins qu'une ou toutes les voitures.

La prochaine fois que j'ouvre mon application, je souhaite actualiser la copie persistante de Core Data en appelant à nouveau le service Web pour toutes les voitures. Toutefois, pour éviter les doublons, je devrais d'abord purger toutes les données du cache local.

Existe-t-il un moyen plus rapide de purger TOUTES les instances d’une entité spécifique dans le contexte de l’objet géré (par exemple, toutes les entités de type "CAR") ou dois-je les interroger, puis parcourir les résultats pour les supprimer, puis les sauvegarder?

Idéalement, je pourrais simplement dire supprimer tout où l'entité est Blah.

359
Adaromas

iOS 9 et versions ultérieures:

iOS 9 a ajouté une nouvelle classe appelée NSBatchDeleteRequest qui vous permet de supprimer facilement des objets correspondant à un prédicat sans avoir à les charger tous en mémoire. Voici comment vous l'utiliseriez:

Swift 2

let fetchRequest = NSFetchRequest(entityName: "Car")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)

do {
    try myPersistentStoreCoordinator.executeRequest(deleteRequest, withContext: myContext)
} catch let error as NSError {
    // TODO: handle the error
}

Objectif c

NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"Car"];
NSBatchDeleteRequest *delete = [[NSBatchDeleteRequest alloc] initWithFetchRequest:request];

NSError *deleteError = nil;
[myPersistentStoreCoordinator executeRequest:delete withContext:myContext error:&deleteError];

Pour plus d'informations sur les suppressions de lots, reportez-vous à la session "Nouveautés des données de base" de WWDC 2015 (à partir de ~ 14: 10).

iOS 8 et versions antérieures:

Tout chercher et tout supprimer:

NSFetchRequest *allCars = [[NSFetchRequest alloc] init];
[allCars setEntity:[NSEntityDescription entityForName:@"Car" inManagedObjectContext:myContext]];
[allCars setIncludesPropertyValues:NO]; //only fetch the managedObjectID

NSError *error = nil;
NSArray *cars = [myContext executeFetchRequest:allCars error:&error];
[allCars release];
//error handling goes here
for (NSManagedObject *car in cars) {
  [myContext deleteObject:car];
}
NSError *saveError = nil;
[myContext save:&saveError];
//more error handling here
685
Dave DeLong

Un peu plus propre et universel: Ajoutez cette méthode:

- (void)deleteAllEntities:(NSString *)nameEntity
{
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:nameEntity];
    [fetchRequest setIncludesPropertyValues:NO]; //only fetch the managedObjectID

    NSError *error;
    NSArray *fetchedObjects = [theContext executeFetchRequest:fetchRequest error:&error];
    for (NSManagedObject *object in fetchedObjects)
    {
        [theContext deleteObject:object];
    }

    error = nil;
    [theContext save:&error];
}
31
Numereyes

Réinitialiser l'entité dans Swift 3

func resetAllRecords(in entity : String) // entity = Your_Entity_Name
    {

        let context = ( UIApplication.shared.delegate as! AppDelegate ).persistentContainer.viewContext
        let deleteFetch = NSFetchRequest<NSFetchRequestResult>(entityName: entity)
        let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetch)
        do
        {
            try context.execute(deleteRequest)
            try context.save()
        }
        catch
        {
            print ("There was an error")
        }
    }
27
roy

Pour Swift 2.0:

class func clearCoreData(entity:String) {
  let fetchRequest = NSFetchRequest()
  fetchRequest.entity = NSEntityDescription.entityForName(entity, inManagedObjectContext: moc!)
  fetchRequest.includesPropertyValues = false
  do {
    if let results = try moc!.executeFetchRequest(fetchRequest) as? [NSManagedObject] {
      for result in results {
        moc!.deleteObject(result)
      }

      try moc!.save()
    }
  } catch {
    LOG.debug("failed to clear core data")
  }
}
16
Gaurav Sharma

Rapide:

let fetchRequest = NSFetchRequest()
fetchRequest.entity = NSEntityDescription.entityForName(entityName, inManagedObjectContext: context)
fetchRequest.includesPropertyValues = false

var error:NSError?
if let results = context.executeFetchRequest(fetchRequest, error: &error) as? [NSManagedObject] {
    for result in results {
        context.deleteObject(result)
    }

    var error:NSError?
    if context.save(&error) {
        // do something after save

    } else if let error = error {
        println(error.userInfo)
    }

} else if let error = error {
    println("error: \(error)")
}
12
Ixx

Ceci est une question similaire à celle ici et quelqu'un a suggéré de configurer une règle de suppression de relation afin que vous ne supprimiez qu'un seul objet. Ainsi, si vous avez ou pouvez créer une entité avec une relation à plusieurs sur les voitures et définir la règle de suppression sur cascade lorsque vous supprimez l'entité supérieure, toutes les voitures seront également supprimées. Cela peut vous faire gagner du temps, car vous n'avez pas à suivre les étapes de chargement de TOUS les wagons. Dans un plus grand ensemble de données, cela pourrait être absolument nécessaire. 

9
T. Markle

Une bonne réponse était déjà postée, ce n'est qu'une recommandation!

Un bon moyen serait d’ajouter une catégorie à NSManagedObject et d’implémenter une méthode comme celle que j’ai faite:

Fichier d'en-tête (par exemple NSManagedObject+Ext.h)

@interface NSManagedObject (Logic)

+ (void) deleteAllFromEntity:(NSString*) entityName;

@end

Fichier de code: (par exemple, NSManagedObject + Ext.m)

@implementation NSManagedObject (Logic)

+ (void) deleteAllFromEntity:(NSString *)entityName {
    NSManagedObjectContext *managedObjectContext = [AppDelegate managedObjectContext];
    NSFetchRequest * allRecords = [[NSFetchRequest alloc] init];
    [allRecords setEntity:[NSEntityDescription entityForName:entityName inManagedObjectContext:managedObjectContext]];
    [allRecords setIncludesPropertyValues:NO];
    NSError * error = nil;
    NSArray * result = [managedObjectContext executeFetchRequest:allRecords error:&error];
    for (NSManagedObject * profile in result) {
        [managedObjectContext deleteObject:profile];
    }
    NSError *saveError = nil;
    [managedObjectContext save:&saveError];
}

@end

... la seule chose à faire est d'obtenir le managedObjectContext auprès du délégué de l'application, ou à tout endroit où vous l'avez;)

ensuite, vous pouvez l'utiliser comme:

[NSManagedObject deleteAllFromEntity:@"EntityName"];

une autre optimisation pourrait consister à supprimer le paramètre pour nom_entité et obtenir le nom à la place du nom_clazz. cela conduirait à l'utilisation:

[ClazzName deleteAllFromEntity];

un impl plus propre (en tant que catégorie à NSManagedObjectContext):

@implementation NSManagedObjectContext (Logic)

- (void) deleteAllFromEntity:(NSString *)entityName {
    NSFetchRequest * allRecords = [[NSFetchRequest alloc] init];
    [allRecords setEntity:[NSEntityDescription entityForName:entityName inManagedObjectContext:self]];
    [allRecords setIncludesPropertyValues:NO];
    NSError * error = nil;
    NSArray * result = [self executeFetchRequest:allRecords error:&error];
    for (NSManagedObject * profile in result) {
        [self deleteObject:profile];
    }
    NSError *saveError = nil;
    [self save:&saveError];
}

@end

L'usage alors:

[managedObjectContext deleteAllFromEntity:@"EntityName"];
8
Erhard Dinhobl

iOS 10 et versions ultérieures

Fonctionne avec toutes les versions. Transmettez le nom de l’entité et parcourez-le pour supprimer toutes les entrées et enregistrer le contexte.

func deleteData(entityToFetch: String, completion: @escaping(_ returned: Bool) ->()) {
        let context = NSManagedObjectContext()
        context = your managedObjectContext

        let fetchRequest = NSFetchRequest<NSFetchRequestResult>()
        fetchRequest.entity = NSEntityDescription.entity(forEntityName: entityToFetch, in: context)
        fetchRequest.includesPropertyValues = false
         do {   
            let results = try context.fetch(fetchRequest) as! [NSManagedObject]
            for result in results {
                context.delete(result)
            }
            try context.save()
            completion(true)
        } catch {
            completion(false)
            print("fetch error -\(error.localizedDescription)")
        }
    }
4
Karun Kumar

Prolonger la réponse de Dave Delong.

Swift Version qui prend en charge iOS 9 et les versions précédentes. J'ai également couvert la gestion des erreurs dans ceci:

let appDelegate: AppDelegate = UIApplication.sharedApplication (). déléguer en tant que! AppDéléguer

    let fetchRequest = NSFetchRequest(entityName: "Car")
    if #available(iOS 9.0, *) {
        let delete = NSBatchDeleteRequest(fetchRequest: fetchRequest)
        do {
            try appDelegate.persistentStoreCoordinator.executeRequest(delete, withContext: appDelegate.managedObjectContext)
        } catch let error as NSError {
            print("Error occured while deleting: \(error)")
        }
    } else {
        // Fallback on earlier versions
        let carRequest = NSFetchRequest()
        carRequest.entity = NSEntityDescription.entityForName("Cars", inManagedObjectContext: appDelegate.managedObjectContext)
        carRequest.includesPropertyValues = false

        do {
            let cars: NSArray = try appDelegate.managedObjectContext.executeFetchRequest(carRequest)

            for car in cars {
                appDelegate.managedObjectContext.delete(car)
            }

            try appDelegate.managedObjectContext.save()

        } catch let error as NSError {
            print("Error occured while fetching or saving: \(error)")
        }
    }
4
Maheen Khalid

Swift 3.X et Swift 4.X , moyen facile. Changer seulement YourTable

    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "YourTable")
    fetchRequest.returnsObjectsAsFaults = false

    do
    {
        let results = try context.fetch(fetchRequest)
        for managedObject in results
        {
            let managedObjectData:NSManagedObject = managedObject as! NSManagedObject
            context.delete(managedObjectData)
        }
    } catch let error as NSError {
        print("Detele all my data in \(entity) error : \(error) \(error.userInfo)")
    }
3
SwiftDeveloper

Mise à jour de Swift 4, iOS 12 et Xcode 10

100% de travail juste couper/coller

Il suffit de mettre cette fonction dans la classe appropriée
et appelez cette fonction self.deleteData() dans viewDidLoad()

ou n'importe où ou sous une fonction ou un bouton de sorte qu'en cliquant sur un bouton, toutes les données de l'entité soient supprimées et remplacent le "myEntity" en tant qu'entité que vous avez définie dans vos données de base

  func deleteData() {

  let appDel:AppDelegate = (UIApplication.shared.delegate as! AppDelegate)
  let context:NSManagedObjectContext = appDel.persistentContainer.viewContext
  let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "myEntity")
        fetchRequest.returnsObjectsAsFaults = false

        do
        {
            let results = try context.fetch(fetchRequest)
            for managedObject in results
            {
                let managedObjectData:NSManagedObject = managedObject as! NSManagedObject
                context.delete(managedObjectData)
            }
        } catch let error as NSError {
            print("Deleted all my data in myEntity error : \(error) \(error.userInfo)")
  }
  }
2
Xcodian Solangi

Pourquoi ne pas incorporer les données que vous recevez avec le cache existant? Autrement, ce n'est pas vraiment 'rafraîchissant', c'est 'recommencer' et vous pouvez également supprimer/supprimer le fichier SQLLite et recommencer (en supposant que vous ne conservez pas d'autres données).

2
AlBlue

si l'entité contient beaucoup d'entrées, le meilleur moyen est comme ça, car cela économise de la mémoire 

 - (void)deleteAll:(NSManagedObjectContext *)managedObjectContext entityName:(NSString *)entityName
{
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    [managedObjectContext setUndoManager:nil];
    NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:managedObjectContext];
    [fetchRequest setEntity:entity];
    [fetchRequest setIncludesPropertyValues:NO];
    [fetchRequest setFetchLimit:100]; // you can change this number if you want
    NSError *error;
    NSArray *items = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
    while ([items count] > 0) {
        @autoreleasepool {
            for (NSManagedObject *item in items) {
                [managedObjectContext deleteObject:item];
            }
            if (![managedObjectContext save:&error]) {
                NSLog(@"Error deleting %@ - error:%@",self.entityName, error);
            }
        }
        items = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
    }
}
1
poyo fever.

Swift 4, iOS 10+
Fonction statique qui peut s’appliquer à toute entité pour supprimer toutes ses données

protocol NSManagedObjectHelper {
}
extension NSManagedObject: NSManagedObjectHelper {
}
extension NSManagedObjectHelper where Self: NSManagedObject {
    static func removeAllObjectsInContext(_ managedContext: NSManagedObjectContext) {
        let request: NSFetchRequest = Self.fetchRequest()
        let deleteRequest = NSBatchDeleteRequest(fetchRequest: request)
        do {
            deleteRequest.resultType = .resultTypeObjectIDs//to clear objects from memory
            let result = try managedContext.execute(deleteRequest) as? NSBatchDeleteResult
            if let objectIDArray = result?.result as? [NSManagedObjectID] {
                let changes = [NSDeletedObjectsKey : objectIDArray]
                /*By calling mergeChangesFromRemoteContextSave, all of the NSManagedObjectContext instances that are referenced will be notified that the list of entities referenced with the NSManagedObjectID array have been deleted and that the objects in memory are stale. This causes the referenced NSManagedObjectContext instances to remove any objects in memory that are loaded which match the NSManagedObjectID instances in the array.*/
                NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [managedContext])
            }
            try managedContext.save()
        } catch let error {
            print(error)
        }
    }
}

«Chambre» est une entité

Room.removeAllObjectsInContext(self.persistentContainer.viewContext)
1
jpulikkottil

Dans Swift 3.0

 func deleteAllRecords() {
        //delete all data
        let context = appDelegate.persistentContainer.viewContext

        let deleteFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "YourClassName")
        let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetch)

        do {
            try context.execute(deleteRequest)
            try context.save()
        } catch {
            print ("There was an error")
        }
    }
1
Rob-4608

Ce code fonctionnera à la fois pour iOS 9 et inférieur

class func deleteAllRecords(in entity : String) // entity = Your_Entity_Name
    {

        let context = CoreDataStack.getContext() // Note:- Replace your context here with CoreDataStack.getContext()
        let deleteFetch = NSFetchRequest<NSFetchRequestResult>(entityName: entity)
        if #available(iOS 9, *)
        {
            let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetch)
            do
            {
                try context.execute(deleteRequest)
                try context.save()
            }
            catch
            {
                print("There was an error:\(error)")
            }
        }
        else
        {
            do{
                let deleteRequest = try context.fetch(deleteFetch)
                for anItem in deleteRequest {
                    context.delete(anItem as! NSManagedObject)
                }
            }
            catch
            {
                print("There was an error:\(error)")
            }
        }
        CoreDataStack.saveContext() // Note:- Replace your savecontext here with CoreDataStack.saveContext()
    }
1
Varun Naharia
    func deleteAll(entityName: String) {

    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
    let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
    deleteRequest.resultType = .resultTypeObjectIDs
    guard let context = self.container?.viewContext
        else { print("error in deleteAll")
            return }

    do {
        let result = try context.execute(deleteRequest) as? NSBatchDeleteResult
        let objectIDArray = result?.result as? [NSManagedObjectID]
        let changes: [AnyHashable : Any] = [NSDeletedObjectsKey : objectIDArray as Any]
        NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [context])
    } catch {
        print(error.localizedDescription)
    }
}
0
Matt Bearson

purge rapide de tous les objets dans DB:

func purgeAllData() {
    let uniqueNames = persistentContainer.managedObjectModel.entities.compactMap({ $0.name })

    uniqueNames.forEach { (name) in
      let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: name)
       let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
         do {
        try persistentContainer.viewContext.execute(batchDeleteRequest)
      } catch {
        let nserror = error as NSError
        fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
      }
   }
 }
0
gbk

Solution Swift 3 avec iOS 9 'NSBatchDeleteRequest' et reprise des versions antérieures de iOS mises en œuvre comme une extension sur 'NSManagedObjectContext'. Référence Apple https://developer.Apple.com/library/content/featuredarticles/CoreData_Batch_Guide/BatchDeletes/BatchDeletes.html

extension NSManagedObjectContext {
    func batchDeleteEntities<T: NSManagedObject>(ofType type: T.Type) throws {
        let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: String(describing: type.self))
        if #available(iOS 9.0, *) {
            let request = NSBatchDeleteRequest(fetchRequest: fetchRequest)
            let result = try execute(request) as? NSBatchDeleteResult
            if let objectIDArray = result?.result as? [NSManagedObjectID] {
                let changes = [NSDeletedObjectsKey: objectIDArray]
                NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [self])
            }
        } else {
            fetchRequest.includesPropertyValues = false
            let results = try fetch(fetchRequest)
            if let actualResults = results as? [NSManagedObject], !actualResults.isEmpty {
                actualResults.forEach { delete($0) }
            }
        }
    }
}
0
chriswillow

Utilisez NSBatchDeleteRequest pour supprimer plusieurs enregistrements si iOS minimum est 9.0. Si le thread d'arrière-plan, exécutez NSManagedObjectContext save sinon utilisez NSFetchRequest pour obtenir des enregistrements et supprimer tous les enregistrements dans la boucle for et une fois la suppression terminée.

0
Jeetendra Kumar

dans iOS 11.3 et Swift 4.1

let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
        let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest )
        batchDeleteRequest.resultType = .resultTypeCount
        do {
            let batchDeleteResult = try dataController.viewContext.execute(batchDeleteRequest) as! NSBatchDeleteResult
            print("The batch delete request has deleted \(batchDeleteResult.result!) records.")
            dataController.viewContext.reset() // reset managed object context (need it for working)
        } catch {
            let updateError = error as NSError
            print("\(updateError), \(updateError.userInfo)")
        }

vous devez appeler reset après exécution. Sinon, il ne sera pas mis à jour dans la vue de table.

0
tien113

iOS 9.0 et versions ultérieures:

NSBatchDeleteRequest est utilisé pour supprimer des enregistrements dans les données de base. Cela fonctionne très rapidement et prend moins de temps pour supprimer tous les enregistrements d'une entité. Il nécessite NSFetchRequest en argument. Si vous souhaitez supprimer tous les enregistrements d'une entité, vous pouvez l'utiliser et cela fonctionne pour moi. 

let manageObject:NSManagedObjectContext = appDelegateObject.managedObjectContext

let fetchRequest = NSFetchRequest(entityName: “EnityName”)

let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)

let persistCor:NSPersistentStoreCoordinator = appDelegateObject.persistentObject
 do {
        try persistCor.executeRequest(deleteRequest, withContext: manageObject)
        try manageObject.save()
    } catch {
        print(error?.localizedDescription)
    }
0
MARK IOS Developer

La réponse Swift 2.0 de Dave Delongs se bloquait pour moi (sous iOS 9)

Mais cela a fonctionné:

let fetchRequest = NSFetchRequest(entityName: "Car")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)

    do {
        try managedObjectContext.executeRequest(deleteRequest)
        try managedObjectContext.save()
    }
    catch let error as NSError {
       // Handle error
    }
0
g_low