Update NSManagedObjectContext - ios

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;
}

Related

Delete specific data from specific entity from core data in objective c?

I have two entity in my core data named "History" and "Favorite". i want to delete some specific data like "Dhaka" from "History" entity. How to delete this. Thanks in advance.
Hope it will help you.
NSString* aType = #"History";
NSString *tp = #"Dhaka";
NSFetchRequest * request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:aType inManagedObjectContext:self.managedObjectContext]];
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"(type == %#)",tp ]; //where type is the attribute
[request setPredicate:predicate];
NSArray* ar = [self.managedObjectContext executeFetchRequest:request error:nil];
NSLog(#"############# Data from DB : %#",ar);
for(id anObj in ar){
[managedObjectContext deleteObject:anObj];
}
[self.managedObjectContext save:nil];
predicate=nil;
try this:
objAppDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
NSManagedObjectContext *context = [objAppDelegate managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:#"History"];
NSArray *recordArray =[[NSArray alloc]initWithArray:[context executeFetchRequest:request error:nil]];
objDataClass = [recordArray objectAtIndex:0]; // here objDataClass is a instance of DataClass derived from NSObject which define column name
[context deleteObject:objDataClass]; // Delete particular data with index
if ([context hasChanges])
{
[context save:nil];
}
else
{
NSLog(#"Object Deleted.....");
}
Hope it will help you. :)

Why coredata is returning null array while I am inserting values in table

I have two methods,
In first method, I do save values in Core Data, while in other, I simply fetch them.
After inserting, when I fetch data in same method, it shows value, but when I try to fetch in other method if returns me null.
My saving Method is
-(void) saveloginData:(NSString *)facebookTok username:(NSString *)userName password:(NSString*)password flag:(NSString *)flag {
NSError *error;
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest * fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"SignIn" inManagedObjectContext:context]];
[fetchRequest setIncludesPropertyValues:NO]; //only fetch the managedObjectID
NSString *facebookTokenData = facebookTok;
NSString *usernameData = userName;
NSString *passwordData = password;
NSString *flagData = flag;
NSLog(#"Facebook Token%#\nUsername%#\npassword%#\nflat%#\n",facebookTokenData,usernameData,passwordData,flagData);
SignIn *signIn = [NSEntityDescription
insertNewObjectForEntityForName:#"SignIn"
inManagedObjectContext:context];
signIn.facebookToken = facebookTokenData;
signIn.username = usernameData;
signIn.password = passwordData;
signIn.flag = flagData;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"SignIn"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedArray = [context executeFetchRequest:fetchRequest error:&error];
for (SignIn *info in fetchedArray) {
\\ THis executes and shows values, proves that value are inserted.
NSLog(#"Name ~~~~ : %#", info.username);
NSLog(#"Password ~~~~~~~~ :%#", info.password);
NSLog(#"FLAG ~~~~~~~~~~~ %#",info.flag);
NSLog(#"Facebook Token %#", info.facebookToken);
}
}
My retrieve Method is
-(NSArray*) getLoginData {
NSError *error;
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"SignIn"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedData = [[NSArray alloc] init];
fetchedData = [context executeFetchRequest:fetchRequest error:&error];
NSLog(#"The count of Array %d", [fetchedData count]); \\ HERE COUNT IS ZERO, WHY?
for (SignIn *info in fetchedData) {
NSLog(#" FF Name ~~~~ : %#", info.username);
NSLog(#"Password ~~~~~~~~ :%#", info.password);
NSLog(#"FLAG ~~~~~~~~~~~ %#",info.flag);
NSLog(#"Facebook Token %#", info.facebookToken);
}
return fetchedData;
}
Please guide that where I am doing mistake.
Your problem is that you need to save conext to get the entity it later.
NSManagedObjectContext save: Attempts to commit unsaved changes to registered objects to their persistent store.
- (BOOL)save:(NSError **)error
Parameters: error: A pointer to an NSError object. You do not need to create an NSError object. The save operation aborts after the first failure if you pass NULL.
Return Value YES if the save succeeds, otherwise NO.
So you need to save context after you modify your object:
signIn.facebookToken = facebookTokenData;
signIn.username = usernameData;
signIn.password = passwordData;
signIn.flag = flagData;
[context save:NULL]; // NULL if you don't need to handle error

ios core data - copy entity records to another entity

I have 2 entity, Units and BUnits, the Units entity have a data that will be replaced many time, and the BUnits will be the backup of the Units entity before clearing it's data.
So, I've created a NSManagedObjectContext instance, then I've retrive an instance for each entity by using
NSManagedObjectContext *context = [mainDelegate managedObjectContext];
NSEntityDescription *unitsEntity = [NSEntityDescription entityForName:#"Units" inManagedObjectContext:context];
NSEntityDescription *bUnitsEntity = [NSEntityDescription entityForName:#"BUnits" inManagedObjectContext:context];
but i've didn't managed to copy the Units entity records to the BUnits entity, other than make a loop for each records, but i believe that there is a better solution.
What do you think about this, is there a better solution?
UPDATE:
The solution i've used in case anyone could use it is in my answer, I think there is a better way to do this, i will keep checking for it and i will update the question if i found anything.
Here is what i've used, using looping for each record:
- (void) copyEntities:(NSString *)sourceEntity And:(NSString *)destinationEntity {
NSManagedObjectContext *context = [mainDelegate managedObjectContext];
NSEntityDescription *Units = [NSEntityDescription entityForName:sourceEntity inManagedObjectContext:context];
NSEntityDescription *BUnits = [NSEntityDescription entityForName:destinationEntity inManagedObjectContext:context];
NSFetchRequest *dataRequest = [[NSFetchRequest alloc] init];
[dataRequest setEntity:Units];
NSError *error = nil;
NSArray *dataArray = [context executeFetchRequest:dataRequest error:&error];
for (UnitsClass *unit in dataArray) {
UnitsClass *savedUnits;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:BUnits];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"(code = %#)", unit.code];
NSPredicate *pred1 = [NSPredicate predicateWithFormat:#"(code2 = %#)", unit.code2];
NSPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:pred, pred1, nil]];
[request setPredicate:compoundPredicate];
NSError *error = nil;
NSArray *objects = [context executeFetchRequest:request error:&error];
//this if to indicate if we inserting to the BUnits or updating it.
if ([objects count] > 0) {
//Found the record, update The info
savedUnits = [objects objectAtIndex:0];
} else {
savedUnits = [NSEntityDescription insertNewObjectForEntityForName:destinationEntity inManagedObjectContext:context];
}
savedUnits.code = unit.code;
/*Add your updated info*/
NSError *errors;
if (![context save:&errors]) {
NSLog(#"Whoops, couldn't save: %#", [errors localizedDescription]);
}
}
NSLog(#"BUnits count = %d",[context countForFetchRequest:[NSFetchRequest fetchRequestWithEntityName:destinationEntity] error:&error]);
}
Based on the given information, there is no better way than looping each unit object and creating the backup object.

Core Data - update entity in background thread automatically changes NSManagedObject in Main Thread without merging- why?

i´m currently learning Core Data. Core Data is great but i can not explain the behaviour with a second managed object context in a background thread.
I have an entity called TestEntity with 2 attributes (testId and testDescription)
On the main thread i fetch the entity with the testId = 1 and store this managed object into an instance variable.
NSEntityDescription *entityDescription = [NSEntityDescription
entityForName:#"TestEntity" inManagedObjectContext:self.managedObjectContext];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];
NSNumber *testId = [NSNumber numberWithInt:1];
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"testId == %#", testId];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *array = [self.managedObjectContext executeFetchRequest:request error:&error];
t1 = [[array objectAtIndex:0] retain];
TestThread *tt = [TestThread new];
NSOperationQueue *queue = [NSOperationQueue new];
[queue addOperation:tt];
[queue waitUntilAllOperationsAreFinished];
NSLog(#"%#", [t1 valueForKey:#"testDescription"]);
Then is start a NSOperation with a NSOperationQueue called TestThread.
In the main method of this TestThread i create a second managed object context, fetch the same entity (testId = 1) like the main thread, change the testDescription property and save the new context without any errors.
tgAppDelegate *delegate = [[NSApplication sharedApplication] delegate];
self.context = [[[NSManagedObjectContext alloc] init] autorelease];
[context setPersistentStoreCoordinator:delegate.persistentStoreCoordinator];
NSEntityDescription *entityDescription = [NSEntityDescription
entityForName:#"TestEntity" inManagedObjectContext:self.context];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];
// Set example predicate and sort orderings...
NSNumber *testId = [NSNumber numberWithInt:1];
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"testId == %#", testId];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *array = [context executeFetchRequest:request error:&error];
TestEntity *t1 = [array objectAtIndex:0];
t1.testDescription = #"abc";
[context save:&error];
if (error) {
// do something
}
The behaviour i can not explain my self is, that the NSLog output after
[queue waitUntilAllOperationsAreFinished];
has the same string value which updated in my background thread.
abc
My understanding of Core Data and Multithreading is that i have to do an explicit merge between contextes.
In my test app there is no merge.
Can anyone explain why this happenes?
By default, a fetch request will return objects as faults so their content is not actually loaded until you access it (you can learn more about faulting under this link).
To see your expected behavior, try logging the original value of t1.testDescription before you start the second thread.
You can also set
self.context.returnObjectsAsFaults = NO;
but this might have a negative impact on the memory footprint of your app.

Pro Core Data book, example code question

I'm learning Core Data with the help of the book. There is a code:
- (void)loadData {
// Pull the movies. If we have 200, assume our db is set up.
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"Movie"
inManagedObjectContext:context]];
NSArray *results = [context executeFetchRequest:request error:nil];
if ([results count] != 200) {
// Add 200 actors, movies, and studios
for (int i = 1; i <= 200; i++) {
[self insertObjectForName:#"Actor" withName:
[NSString stringWithFormat: #"Actor %d", i]];
[self insertObjectForName:#"Movie" withName:
[NSString stringWithFormat: #"Movie %d", i]];
[self insertObjectForName:#"Studio" withName:
[NSString stringWithFormat: #"Studio %d", i]];
}
// Relate all the actors and all the studios to all the movies
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"Movie"
inManagedObjectContext:context]];
NSArray *results = [context executeFetchRequest:request error:nil];
for (NSManagedObject *movie in results) {
[request setEntity:[NSEntityDescription entityForName:#"Actor"
inManagedObjectContext:context]];
NSArray *actors = [context executeFetchRequest:request error:nil];
NSMutableSet *set = [movie mutableSetValueForKey:#"actors"];
[set addObjectsFromArray:actors];
[request setEntity:[NSEntityDescription entityForName:#"Studio"
inManagedObjectContext:context]];
NSArray *studios = [context executeFetchRequest:request error:nil];
set = [movie mutableSetValueForKey:#"studios"];
[set addObjectsFromArray:studios];
}
}
[request release];
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
The main question is: is it necessary to renew the context pointer, if there were any changes in that context?
What I mean: I get the pointer to the context at the beginning of the method, next in the loop I take this context and insert managed objects there (-insertObjectForName:withName:). Then I see this renewal of the context pointer and have that question: is it a rule of some kind, and I should act the same, or its just not-so-neat code example? Why can't I use the old pointer?
---Edit--- One more question: is that a legal initialization of a request here in the code:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
...
NSFetchRequest *request = [[NSFetchRequest alloc] init];
...
[request release];
Two allocs and only one release?
I also have this book and looked it up.
Clearly seems like a typo to me and doesn't really make much sense.
Just ignore that line and continue - it should work fine without.
In my opinion
its just not-so-neat code example
About your second question: There have to also two releases! Otherwise you have a leak.
That's some seriously ugly code.
This:
NSManagedObjectContext *context = [self managedObjectContext];
... is probably just because they want to be able to write context in a method call instead of self.managedObjectContext.
Using:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
... twice is allowed but it is lazy bad practice. Any variable should be named only once in a scope. In fact, the compiler will generate a warning with this code. It will leak because every init must be balanced by a release.

Resources