I have a Project object that has a to-many relationship to Image, the delete rules are set up as Cascade for project.images and Nullify for image.project. In this case I need to clear out the attached images but leave the project itself intact. There are a lot of images so I want to use a batched delete to get them all in one go.
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"Image"];
request.predicate = [NSPredicate predicateWithFormat:#"project = %#", project];
NSBatchDeleteRequest *delete = [[NSBatchDeleteRequest alloc] initWithFetchRequest:request];
delete.resultType = NSBatchDeleteResultTypeObjectIDs;
NSError *error;
NSBatchDeleteResult *result = [context.persistentStoreCoordinator executeRequest:delete withContext:context error:&error];
if (error) {
// todo
}
[NSManagedObjectContext mergeChangesFromRemoteContextSave:#{NSDeletedObjectsKey : [result result]} intoContexts:#[context]];
NSLog(#"images: %#", project.images); // Still has all the images (they are now flagged as isDeleted)
[context save:&error];
NSLog(#"images: %#", project.images); // Still has all the images...
According to the docs the mergeChangesFromRemoteContextSave line should take care of updating the context but this doesn't seem to happen.
One more thing, I know I can set project.images = nil and this does the job, but can't be used in a case when I am only deleting a subset of the images.
Edit: I have investigated further and have more info.
The issue occurs when project.images are faults. If I force them all to fault in then the relationships will successfully updated when the delete goes through. If I leave them as faults then the relationship is untouched and will now point to non-existent objects.
In mergeChangesFromRemoteContextSave try including the NSUpdatedObjectsKey with the project's objectID.
[NSManagedObjectContext mergeChangesFromRemoteContextSave:#{NSUpdatedObjectsKey : #[project.objectID], NSDeletedObjectsKey : [result result]} intoContexts:#[context]];`
Related
I want to provide user a functionality to delete single or multiple messages at a time using long-tap/select action.
I know you want to know what I have tried so far. But the thing is I haven't found anything regarding deleting messages to implement.
Any kind of help is appreciated!
You have to delete message from xmpp core database.
So xmpp had created "XMPPMessageArchiving_Message_CoreDataObject" named core database table and using this you can delete message.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"XMPPMessageArchiving_Message_CoreDataObject" inManagedObjectContext:myAppdelObject.Obj_xmppManager.moc];
[fetchRequest setEntity:entity];
NSError *error;
NSArray *items = [mocObject executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *managedObject in items) {
[mocObject deleteObject:managedObject];
}
#Parthpatel1105's answer is right, though as #Bista says, it will not delete the messages permanently.
After performing the deletion, any deletion, either full deletion as #Parthpatel1105 does, or a single message, which would be the same but without the for loop and you'd have to find the single message to delete. You HAVE to save the storage context.
Which means, after doing:
for (NSManagedObject *managedObject in items) {
[mocObject deleteObject:managedObject];
}
You have to add a call to save,
In Swift (where I've used it):
mocObject.save()
In Objective-C, I think it would be something like:
[mocObject save:&error];
I have a relatively simple entity. When I create it, set its attributes, and save it, it saves successfully. I can later retrieve it and it is not nil and I get a successful save message from MagicalRecord.
When I retrieve it and try to access any attribute though the attribute is nil. The entity itself is fine but the attributes are all nil. I have checked they are all definitely set correctly before I save.
I haven't encountered this problem before. Why could it be occurring?
NB: This doesn't happen every time. Most times I call the method to create and save this entity it can later be retrieved without any issues. The problem is intermittent but possible to replicate on every run.
Code:
Entity1 *entity1 = [Entity1 MR_createEntityInContext:localContext];
[entity1 setUpEntity:myobject];
EntityChild *entityChild=[EntityChild MR_createEntityInContext:localContext];
[entityChild setUpEntityChild:entity.child withContext:localContext];
[entityChild setEntity1:entity1];
[localContext MR_saveToPersistentStoreWithCompletion:^(BOOL success, NSError *error) {
}];
Update:
If I look in the sqlite database and search for the entity it actually doesn't exist at all. So MagicalRecord tells me it saves, CoreData lets me retrieve a non-nil object (albeit with nil attributes) but no record exists in the database.
I did not understand ur code standards. As I am new to IOS Development. I Used below code for retrieving.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entityRef = [NSEntityDescription entityForName:#"Entity1" inManagedObjectContext:appDelegate.managedObjectContext];//localContext
[fetchRequest setEntity:entityRef];
NSError *error=nil;
NSArray *detailsArray = [appDelegate.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (error) {
NSLog(#"Unable to execute fetch request.");
NSLog(#"%#, %#", error, error.localizedDescription);
}
Saving the data
NSManagedObjectContext *context = [appDelegate managedObjectContext];//localContext
NSManagedObject *objectRef = [NSEntityDescription
insertNewObjectForEntityForName:#"Entity1" inManagedObjectContext:context];
[objectRef setValue:#"IOS" forKey:#"Name"];
[objectRef setValue:#"positive" forKey:#"Attitude"];
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
Hope it helps you...!
Ok, I got to the bottom of this. It wasn't a problem with the code when I did the save. It was actually a problem with some code in another class that was retrieving the data from the wrong context. When I changed the context it worked correctly.
I'm still not sure why this only happened occasionally and not every time the code was run but it's working now.
Thanks for your help anyway everyone.
My question is similar to the following one but has differences
I use Core Data. The problem is if I use the current NSManagedObjectContext then I may get an incorrect result. For example:
From the beginning the db is filled.
My app deletes all the entities via the current context.
I get check if db filled and get NO but in reality the db is still filled because the context should be saved to apply the changes.
And example of my solution:
- (BOOL)isDBFilled {
//separate context to
NSManagedObjectContext *context = [[[NSManagedObjectContext alloc] init] autorelease];
[context setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
[context setPersistentStoreCoordinator:coordinator];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
NSEntityDescription *entity = [NSEntityDescription entityForName:... inManagedObjectContext:context];
[request setEntity:entity];
[request setFetchLimit:1];
NSError *error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
if (!results) {
LOG(#"Fetch error: %#", error);
abort();
}
if ([results count] == 0) {
return NO;
}
return YES;
}
So is it normal and safe to create another context just to check if db is filled? Or Is there better ways to perform this checking except of creating a separate BOOL variable which will be changed before db changes and after them?
It is fine to use a separate context to check if the database is populated or not. The overhead is minimal, so I would have no objections.
Please note that Apple does not necessarily agree. From the "Core Data Snippets":
Neither should a view controller create a context for its own use (unless it’s a nested context).
That being said, I do not see the need for this. If you delete all entities, save and then check should get an empty database. If you delete all entities and do not save and then check, you should get a non-empty database. You can easily - and more easily - do this with one context.
PS: autorelease seems to indicate that you are using a very old SDK. How about upgrading?
I have seen this link where countforfetch is always 1. But doesn't give the solution.
When i do a fetch request as given in the link it gives me the data i was about to save every time. So since its already present it wont re-save. So the database is empty. But surprisingly the data comes on the table.
This seems like a very weird behaviour. Can some please help ?
here is my code
NSFetchRequest *fetchRequest12 = [[NSFetchRequest alloc] init];
NSError *error;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"orderNumber = %#",orderList.orderNumber];
[fetchRequest12 setPredicate:predicate];
[fetchRequest12 setEntity:[NSEntityDescription entityForName:#"OrderList" inManagedObjectContext:appDelegate.managedObjectContext]];
NSLog(#"The fetch was successful! %#", [appDelegate.managedObjectContext executeFetchRequest:fetchRequest12 error:&error]);
if ([appDelegate.managedObjectContext countForFetchRequest:fetchRequest12 error:&error] ==0 ) { // Somethings to be done
}
Use setIncludesPendingChanges: to NO if you want the fetch request to ignore any changes that you have made in the MOC but not yet saved. By default all unsaved changes are fetched (hence you see unsaved changes displayed in your UI).
Let's say I load in 1,000 objects via Core Data, and each of them has a user-settable Favorite boolean. It defaults to NO for all objects, but the user can paw through at will, setting it to YES for as many as they like. I want a button in my Settings page to reset that Favorite status to NO for every single object.
What's the best way to do that? Can I iterate through every instance of that Entity somehow, to set it back? (Incidentally, is 'instance' the right word to refer to objects of a certain entity?) Is there a better approach here? I don't want to reload the data from its initial source, since other things in there may have changed: it's not a total reset, just a 'Mass Unfavourite' option.
Okay, I've gotten it to work, but it requires a restart of the app for some reason. I'm doing this:
- (IBAction) resetFavourites: (id) sender
{
NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
[fetch setEntity: [NSEntityDescription entityForName: #"Quote" inManagedObjectContext: [[FQCoreDataController sharedCoreDataController] managedObjectContext]]];
NSArray *results = [[[FQCoreDataController sharedCoreDataController] managedObjectContext] executeFetchRequest: fetch error: nil];
for (Quote *quote in results) {
[quote setIsFavourite: [NSNumber numberWithBool: NO]];
}
NSError *error;
if (![self.fetchedResultsController performFetch: &error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
[self.tableView reloadData];
}
This works fine if I close and re-open the app, but it isn't reflected immediately. Isn't that what the reloadData should do, cause an immediate refresh? Am I missing something there?