I use core data in my application. When I delete object with [context deleteObject:obj], object is not deleted only with empty fields. How can I delete object permanently? Thanks in advanced. I use table view and when this view is filled with data from core data, there are empty rows where were deleted objects. This is the code:
LNAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
self.context = [appDelegate managedObjectContext];
self.accounts = [[NSArray alloc] init];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Accounts" inManagedObjectContext:self.context];
[fetchRequest setEntity:entity];
NSError *error;
self.accounts = [self.context executeFetchRequest:fetchRequest error:&error];
Are you making sure to save the context after you make the change?
NSError *error = nil;
if(![self.managedObjectContext save:&error])
{
/*
//Handle error
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
Also make sure that you are referencing the correct context, as you can have more than one.
Related
Please consider the method at the end of this question. It attempts to delete all records from a CoreData entity.
The first -outcommented- part works fine: it deletes everything from my context, and then saves the context.
The second, non-commented part doesn't seem to work:
As a test I do
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"myEntity"];
NSError *error = nil;
NSArray *results = [self.managedObjectContext executeFetchRequest:request error:&error];
NSLog(#"Fetch request returns %lu objects", (unsigned long)[results count]);
When I add records, and delete all records with the method below, the amount of records only increase.
As far as I understand, it actually works, but the context is not aware of it (yet).
So, therefore, in order to get a reliable context, I should update the context with
[NSManagedObjectContext mergeChangesFromRemoteContextSave:<#(nonnull NSDictionary *)#> intoContexts:self.managedObjectContext];
However, I don't have a clue what I should enter in the (nonnull NSDictionary *) part.
BTW: I only want to use the NSBatchDeleteRequest because I assume it is faster than iterating through all records.
- (void)deleteAllEntities:(NSString *)nameEntity
{
id appDelegate = (id)[[UIApplication sharedApplication] delegate];
self.managedObjectContext= [appDelegate managedObjectContext];
/*
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:nameEntity];
[fetchRequest setIncludesPropertyValues:NO]; //only fetch the managedObjectID
NSError *error;
NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *object in fetchedObjects)
{
[self.managedObjectContext deleteObject:object];
}
error = nil;
[self.managedObjectContext save:&error];
*/
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:nameEntity];
NSBatchDeleteRequest *delete = [[NSBatchDeleteRequest alloc] initWithFetchRequest:request];
NSError *deleteError = nil;
}
I hope it's a duplicate of other links like: How to update existing object in Core Data?
But my scenario is little bit different.
The thing is I created one Sample Core Data Project.
Step 1: I have 3 ViewController classes
In 1st Screen: I created one field in core data "password" ==> I saved using code like:
NSManagedObjectContext *context = [self managedObjectContext];
// Create a new userInfo
NSManagedObject *newuserInfo = [NSEntityDescription insertNewObjectForEntityForName:#"UserInfo" inManagedObjectContext:context];
[newuserInfo setValue:self.passwordTxtFldRef.text forKey:#"password"];
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
// NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
In 2nd Screen: I have two textfields like: Oldpassword & newpassword
. Here I saved old password in NSUserDefaults and in 2nd screen i checked the condition weather the oldpassword is correct or not --> It's perfect.
Here my requirement is I want to update Oldpassword to newpassword ---> in 2nd screen itself.
How can I do this? Can you please help me out regarding this?
Here i use code like:
-(void)getSavedDataFromCoreData{
NSManagedObjectContext *context = [self managedObjectContext];
NSEntityDescription *entitydesc = [NSEntityDescription entityForName:#"UserInfo" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
[request setEntity:entitydesc];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"password like %#",oldPasswordStr];
[request setPredicate:predicate];
NSError *error;
NSArray *matchingData = [context executeFetchRequest:request error:&error];
if(matchingData.count<=0){
//NSLog(#"Cancel");
}else{
for(NSManagedObject *obj in matchingData){
[obj setValue:alertTextField2.text forKey:#"password"];
}
[context save:&error];
NSMutableArray *array3 = [matchingData valueForKey:#"password"];
NSString *value3 = [array3 objectAtIndex:0];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setValue:value3 forKey:#"savedPassword"];
[defaults synchronize];
[self showGeneralAlertMessage:#"Password Changed Successfully" withTitle:#"Success"];
}
}
This will help you to update the value.
-(void)updateDataInCoreData
{
NSManagedObjectContext *context = appDelegate.managedObjectContext;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"User" inManagedObjectContext:context];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"number == %#",userDict[#"number"]];
[fetchRequest setPredicate:predicate];
[fetchRequest setFetchLimit:1];
[fetchRequest setEntity:entity];
NSError *error;
NSArray *arrResult = [context executeFetchRequest:fetchRequest error:&error];
User *entity1 = arrResult[0];
entity1.number = #"123456";
[appDelegate saveContext];
}
I have an entity Country that includes an attribute downloaded which has a default value of 0, and is NOT mapped by RestKit. I want to be able to group my tableView into sections based on this downloaded attribute. Everything works as expected, until I try to programmatically change the downloaded value myself. Code below:
Where I'm trying to set the value - my context is the mainQueueManagedObjectContext that has been passed to the controller through the AppDelegate.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
Country *country = [self.fetchedResultsController objectAtIndexPath:indexPath];
[FGDataCalls downloadFieldGuide:country.countryId];
//Set up to get the thing you want to update
NSFetchRequest * request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"Country" inManagedObjectContext:self.managedObjectContext]];
[request setPredicate:[NSPredicate predicateWithFormat:#"countryId = %#", country.countryId]];
NSError *error = nil;
country = [[self.managedObjectContext executeFetchRequest:request error:&error] lastObject];
if (error) {
NSLog(#"Error getting the country from core data: %#", error);
}
country.downloaded = 1;
error = nil;
if (![self.managedObjectContext save:&error]) {
NSLog(#"Saving changes to country failed: %#", error);
}
}
FetchedResultsController:
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
/*
Set up the fetched results controller.
*/
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Country" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Sort using the name property.
NSSortDescriptor *sortDownloaded = [[NSSortDescriptor alloc] initWithKey:#"downloaded" ascending:NO];
NSSortDescriptor *sortName = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
[fetchRequest setSortDescriptors:#[sortDownloaded, sortName]];
// Use the sectionIdentifier property to group into sections.
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"downloaded" cacheName:nil];
_fetchedResultsController.delegate = self;
self.fetchedResultsController = _fetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return _fetchedResultsController;
}
Initializing Restkit:
//Enable RestKit logging
//RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelInfo);
//RKLogConfigureByName("RestKit/CoreData", RKLogLevelTrace);
//RKLogConfigureByName("RestKit/Network", RKLogLevelTrace);
// Initialize RestKit
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:BaseURLString]];
// Initialize managed object store
NSError *error = nil;
NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"FGDataModel" ofType:#"momd"]];
NSManagedObjectModel *managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
BOOL success = RKEnsureDirectoryExistsAtPath(RKApplicationDataDirectory(), &error);
if (!success) {
RKLogError(#"Failed to create Application Data Directory at path '%#': %#", RKApplicationDataDirectory(), error);
}
objectManager.managedObjectStore = managedObjectStore;
NSString *path = [RKApplicationDataDirectory() stringByAppendingPathComponent:#"FGDataModel.sqlite"];
NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:path fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
if (! persistentStore) {
RKLogError(#"Failed adding persistent store at path '%#': %#", path, error);
}
// Configure a managed object cache to ensure we do not create duplicate objects
managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
[managedObjectStore createManagedObjectContexts];
// Set the default store shared instance
[RKManagedObjectStore setDefaultStore:managedObjectStore];
// Setup our object mappings
[FGDataCalls setupObjectMappings:managedObjectStore withObjectManager:objectManager];
// Save the MOC
AppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
appDelegate.managedObjectContext = managedObjectStore.mainQueueManagedObjectContext;
I've tried saving the context as many different ways at this point, including blocks, and I haven't yet had the fetchedResultsController recognize the update and even call controllerWillChangeContent: or controllerDidChangeContent:. I have successfully deleted a row using the MOC's deleteObject: method, so I have the context in the correct state. I can even get the object from my fetchedResultsController after the downloaded attribute has been set and see that it is correct, but the table never updates. Additionally, forcing the table to reload after I have changed the attribute doesn't yield any result. I know this is a specific case, but hopefully someone else has run into the same issue or can see where I'm going wrong.
Thanks!
When saving you shouldn't do
error = nil;
if (![self.managedObjectContext save:&error]) {
NSLog(#"Saving changes to country failed: %#", error);
}
you should do
error = nil;
if (![self.managedObjectContext saveToPersistentStore:&error]) {
NSLog(#"Saving changes to country failed: %#", error);
}
That said, assuming the FRC is connected to the same context then it should see changes before they're saved. You also don't show your FRC delegate methods so there could be an issue there.
I'm having troubles in updating a specific record in my core data. What happens in my code is it does change the value but when I rerun the app it goes back to its original value. Why that happens?
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Wish" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
// Specify criteria for filtering which objects to fetch
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"itemWish = %#", self.wishItemStr];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
[fetchedObjects setValue:#"YES" forKeyPath:#"isAchieved"];
if (fetchedObjects == nil) {
NSLog(#"no fetched objects!");
}
You need to save the context.
[managedObjectContext save:&error];
I suggest you to read these very good tutorials and articles on objc.io.
I just cant seem to figure out how to update core data object after I fetch the object that I want to modify.
This is what I'm trying to do :
1) Find 1st object from core data matching predicate conditions :
NSInteger storeId = 235; //find object with this id in core data
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:[self.managedObjectContext persistentStoreCoordinator]];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Store"
inManagedObjectContext:context];
[request setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"id == %i",storeId];
[request setPredicate:predicate];
NSArray *results = [context executeFetchRequest:request error:NULL];
2) If match found, update matching object (This is where I need help) :
if (results != nil && [results count] > 0)
{
/** Not sure how to get the correct context and modify object **/
Store *matchingObject = [results objectAtIndex:0];
[context setValue:[NSNumber numberWithInteger:storeId] forKey:"id"];
}
/** Save the context */
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
//abort();
}
Thank you for any help you can provide...
Try modifying the object property directly and saving it:
matchingObject.id = [NSNumber numberWithInteger:storeId];
The object was originally fetched with context so you should be able to save your changes by calling save on context.