I would like to omitt the predicate in an NSFetchRequest in order to delete all managed objects for an entity.
However, when there is no predicate (according to the SQL debugger), the fetch request is not executed. According to Apple the predicate should be optional.
How would I need to change my code to remove the predicate? Any ideas? Thank you!
- (void)deleteEntity:(NSString*)entityName inContext:(NSManagedObjectContext *)context
{
NSFetchRequest * request= [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"TableStructure" inManagedObjectContext:context]];
//[entities setIncludesPropertyValues:NO]; //only fetch the managedObjectID
NSPredicate * predicate = [NSPredicate predicateWithFormat:#"category = 'est'"];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *entitiesArray = [context executeFetchRequest:request error:&error];
if (error) {
NSLog(#"%#: Error fetching context: %#", [self class], [error localizedDescription]);
NSLog(#"entitiesArray: %#",entitiesArray);
return;
}
for(NSManagedObject *entity in entitiesArray) {
[context deleteObject:entity];
}
NSError *saveError = nil;
[context save:&saveError];
}
Update 2
It seems that I call my fetch request before the database is ready. How can I make sure that my request is not called before the core data database is ready?
You simply don't assign the predicate if you don't want it. Remove following lines:
NSPredicate * predicate = [NSPredicate predicateWithFormat:#"category = 'est'"];
[request setPredicate:predicate];
I issued my fetch request before the Core Data database was ready.
To solve this issue, I have now added the call ImportFormulasInRequest to the UIDocument openWithCompletion handler, which encapsulates my core data database:
- (void)useDocument
{
if (![[NSFileManager defaultManager] fileExistsAtPath:[self.myDatabase.fileURL path]]) {
// does not exist on disk, so create it
[self.myDatabase saveToURL:self.myDatabase.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
[self setupFetchedResultsController];
}];
} else if (self.myDatabase.documentState == UIDocumentStateClosed) {
// exists on disk, but we need to open it
[self.myDatabase openWithCompletionHandler:^(BOOL success) {
[self setupFetchedResultsController];
[self ImportFormulasInContext:[self.myDatabase managedObjectContext]];
}];
} else if (self.myDatabase.documentState == UIDocumentStateNormal) {
// already open and ready to use
[self setupFetchedResultsController];
[self ImportFormulasInContext:[self.myDatabase managedObjectContext]];
}
}
Related
I want to update the first record in the NSManagedObject. What I have here updates all of them which I realise is because I am selecting them all and updating them all using the for but how do I just update the first record?
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Users"];
NSError *errorLoading = nil;
self.users = [context executeFetchRequest:fetchRequestNext error:& errorLoading];
for (NSManagedObject *usersObject in [self users])
{
[usersObject setValue:#"*" forKey:#"currentUser"];
}
NSError *error;
[context save:&error3];
}
From the results (array) you simply select the one at index:0
NSArray *results = [context executeFetchRequest:fetchRequestNext error:& errorLoading];
if (results.count>0) {
NSManagedObject *userObject = results[0];
[usersObject setValue:#"*" forKey:#"currentUser"];
}
NSError *saveError = nil;
[context save:&saveError];
I hope this helps
Kind of like this:
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Users"];
fetchRequest.fetchLimit = 1; //Fetch only one object (optional)
NSError *errorLoading = nil;
NSManagedObject *user = [[context executeFetchRequest:fetchRequestNext error:& errorLoading] firstObject];
if (errorLoading) {
//handle error
}
[user setValue:#"*" forKey:#"currentUser"];
NSError *error = nil;
if (![context save:&error]) {
//handle error
}
First of all, if you only need one object, you can set the fetchLimit to 1. It is not required, it's just a small optimization (it will make CoreData stop after fetching the first object). Then you execute the request just like you normally would and get the first object from the resulting array.
Another option is to use the method firstObject defined in NSArray.
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Users"];
NSError *errorLoading = nil;
NSArray *users = [context executeFetchRequest:fetchRequestNext error:& errorLoading];
NSManagedObject *singleUser = [users firstObject];
if(singleUser){
[singleUser setValue:#"*" forKey:#"currentUser"];
}
NSError *error;
if([context save:&error]){
}
Trying to update some Core Data. The data is actually updating "somewhere", but its not saving/updating the db.
- (IBAction)Update:(id)sender {
NSEntityDescription *entityDesc =
[NSEntityDescription entityForName:#"Preferences"
inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesc];
NSError *error;
NSArray *objects = [context executeFetchRequest:request
error:&error];
if ([objects count] == 0) {
// No update, didnt find any entries.
} else {
for (NSManagedObject *obj in objects) {
[obj setValue:_salesPrice.text forKey:#"value"];
if(![context save:&error]){
NSLog(#"Saving changes failed: %#", error);
}
}
}
//[context save:&error];
}
I've tried [context save:&error]; in the commented area, but still no save. I also get no error on save.
You use only 1 NSManagedObjectContext? Your naming convention is not ideal. Usually you would name the entity Preference, since its one object. Try the following code.
CoreDataAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
// This is for completion. Usually you should not get the context from the App Delegate.
// Its better to pass it from the App Delegate to
// the initial view controller via a property (dependency injection).
NSFetchRequest *req = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClass([Preferences class])];
NSError *error = nil;
NSArray *preferences = [context executeFetchRequest:req error:&error];
// Check error
if ([preferences count] == 0) {
// No update, didnt find any entries.
} else {
for (Preferences *preference in preferences) {
[preference setValue:_salesPrice.text forKey:#"value"];
}
}
[context save:&error];
// Check error
Can anyone spot why this isn't returning any ManagedObjects? I'm trying to add to the ATNSManagedObject+EasyFetching class the following, but the fetch result returns nothing. If I fetch these outside of the EasyFetch class I have 100+ objects so I know it isn't CoreData being empty.
+ (void)deleteAllObjectsInContext;
{
NSManagedObjectContext *context = [NSManagedObjectContext defaultContext];
NSEntityDescription *entity = [self entityDescriptionInContext:context];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entity];
//[request setIncludesPropertyValues:NO];
NSError *error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
if (error != nil)
{
//handle errors
NSLog(#"delete error");
}
for (NSManagedObject *thing in results) { <--- |results shows 0 objects|
[context deleteObject:thing];
}
NSError *saveError = nil;
[context save:&saveError];
}
Try simplifying it to:
// first get the context or pass it in as an argument (this is usually what I do for
// a deleteAll class level method like this but your call
+ (void)deleteAllObjectsInContext:(NSManagedObjectContext*)context {
NSFetchRequest* request = [NSFetchRequest fetchRequestWithEntityName:# "<yourEntity>"];
// no predicate
// no sortDescriptors
NSError* error = nil;
NSArray* results = [context executeFetchRequest:request error:&error];
if (!results || error) { // nil is an error
// handle error
}
// do something with results
}
this way you can avoid having to retrieve an NSEntityDescription object.
UPDATE:
Just wanted to add this passage:
Return Value
"An array of objects that meet the criteria specified by request fetched from the receiver and from the persistent stores associated with the receiver’s persistent store coordinator. If an error occurs, returns nil. If no objects match the criteria specified by request, returns an empty array".
One possibility is that your NSManagedObjectContext *context is actually nil.
In Objective-C, sending messages to nil is perfectly acceptable, and can make it hard to detect where an issue is.
I don't find documentation for [NSManagedObjectContext defaultContext], so I assume that is a category you wrote (or are using); and I suspect it is not always returning a valid context. Add some logging and see!
Try this method and watch the Log. It just fetches everything from the current entity.
+ (NSArray*) retrieveEntity:(NSString*) entityName {
// !!!Here you put your context
NSManagedObjectContext *context = appDelegate.managedObjectContext;
if (context == nil) {
NSLog(#"Error: No context");
return nil;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
// Handle the error
NSLog(#"Error: No fetched objects.");
return nil;
}
else
NSLog(#"Retrieved objects count:%d", [fetchedObjects count]);
return fetchedObjects;
}
This is an example, how to call it.
// Retrieve all products
NSArray *flXProducts = [DbConnection retrieveEntity:#"FLXProduct"];
If it returns 0, then there is a problem in your database. You can reveal a problem by finding sql file of your database and tring simple SQL on it in Terminal.
I can't update objects in my database using core data, this my function :
- (void) saveItem:(NSDictionary*)dico {
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Item"
inManagedObjectContext:managedObjectContext];
Item *item =(Item *)[entity ReadSingleForKey:#"identifier"
value:[dico valueForKey:#"identifier"]
inContext:managedObjectContext];
if (!item) {
item = [[[NSManagedObject alloc] initWithEntity:entity
insertIntoManagedObjectContext:managedObjectContext] autorelease];
item.identifier = [dico valueForKey:#"identifier"];
}
item.title = [dico valueForKey:#"title"];
NSError *error = nil;
if (![managedObjectContext save:&error])
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}else{
NSLog(#"No error found.");
}
}
Even if "item" is not nil the object in the database doesn't change & I got always "No error found.".
- (NSManagedObject *) ReadSingleForKey:(NSString *) key
value:(id) value inContext:(NSManagedObjectContext *) context{
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:self];
[request setPredicate:[NSPredicate predicateWithFormat:#"%K = %#", key, value]];
[request setFetchLimit:1];
NSError *error;
NSArray *arr = [context executeFetchRequest:request error:&error];
if (arr && [arr count]) {
return [arr objectAtIndex:0];
}
return nil;
}
Any idea ??
There are several problems with your code that make it difficult to determine the error.
1) No error handling.
2) Obscure private method ReadSingleForKey - what does it return?
3) item defined as type Item and as different type NSManagedObject in same method.
Put in NSLog statements or breakpoints to examine the values of dico and item. You will soon find the place where you go wrong.
Another potential source of this error is how you read the data from the database later. For now I am assuming that this is working correctly.
The problem was in my Item class :
I was using #synthesize instead of #dynamic
I know how to use NSPredicate to perform a SQL SELECT-like operation. How can I perform something like DELETE WHERE? Do I have to call [NSManagedObjectContext deleteObject] for each fetched object? Thanks
NSError *error;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:TASK_ENTITY inManagedObjectContext:managedObjectContext]];
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"label LIKE %#", label];
[request setPredicate:predicate];
NSArray *array = [managedObjectContext executeFetchRequest:request error:&error];
[managedObjectContext deleteObject:[array objectAtIndex:0]];
I believe looping over the returned array and calling [NSManagedObjectContext deleteObject:] is the "correct"/idiomatic way to do it. It might seem inefficient, but remember that the fetch command doesn't actually fetch the objects' data, and the deleteObject: method just marks the object for deletion, which gets applied when you send [NSManagedObjectContext save:]. Not knowing the internals of Core Data I can't tell you whether it's as efficient as a DELETE WHERE query (presumably Core Data has the indexed primary keys in memory from the fetch, and uses those) but in my experience with profiling Core Data apps it's not significantly slower than saving new or updated objects.
You can use NSBatchDeleteRequest available on iOS 9.0+, macOS 10.11+, tvOS 9.0+, watchOS 2.0+
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"label LIKE %#", label];
NSFetchRequest *fetchRequest = [TaskEntity fetchRequest];
[fetchRequest setPredicate:predicate];
// Create batch delete request
NSBatchDeleteRequest *deleteReq = [[NSBatchDeleteRequest alloc] initWithFetchRequest:fetchRequest];
NSError *error = nil;
NSBatchDeleteResult *deletedResult = [appDelegate.persistentContainer.viewContext executeRequest:deleteReq error:&error];
if (error) {
NSLog(#"Unable to delete the data");
}
else {
NSLog(#"%# deleted", deleteReq.result);
}
Swift code (from the above link)
let fetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Employee")
fetch.predicate = NSPredicate(format: "terminationDate < %#", NSDate())
let request = NSBatchDeleteRequest(fetchRequest: fetch)
do {
let result = try moc.execute(request)
} catch {
fatalError("Failed to execute request: \(error)")
}
NOTE:
I found below comment about execute of moc
Method to pass a request to the store without affecting the contents of the managed object context.
Which means any unsaved data in moc won't be affected. i.e. if you've created/updated entity that falls in the delete request criteria and don't called save on moc then that object won't be deleted.
I haven't found an other way than to use an NSArray method for deletion.
(If there is I want to know about it)
You could nest the call to have it on one line if you really don't wan't to store the array. But if you do so verify what is the return of the Fetch in case of an error.
makeObjectsPerformSelector :
Sends to each object in the array the message identified by a given selector, starting with the first object and continuing through the array to the last object.
- (void)makeObjectsPerformSelector:(SEL)aSelector
Or there is a block one to that is suppose to be faster.
I just tried this and it is successful:
NSError *error = nil;
NSFetchRequest* fetchrequest = [NSFetchRequest fetchRequestWithEntityName:#"EntityName"];
[request setPredicate:[NSPredicate predicateWithFormat:#"attribute == %#", variable]];
NSArray *deleteArray = [context executeFetchRequest:fetchrequest error:&error];
if (deleteArray != nil)
{
for (NSManagedObject* object in deleteArray)
{
[context deleteObject:object];
//Reload/refresh table or whatever view..
}
[context save:&error];
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:TASK_ENTITY inManagedObjectContext:managedObjectContext];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"label LIKE%#", label.text]];
NSError* error = nil;
NSArray* results = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
if(![results count]==0)
{
[managedObjectContext deleteObject:[results objectAtIndex:0]];
}
if (![managedObjectContext save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}