Async core data from multiple class and threads - ios

After trying to figure out in my previous question what is the exact problem:
fetchedObjects (NSArray) count return 0 when it's full of objects
I'm pretty sure I need my core data to be async from multiple classes and threads.
I tried multiple calls to my core data in a row, one by one and I had no problem.
But obviously I need it to be read/write from multiple classes and threads.
I trued using #synchronized and still nothing, I've an 0 records in fetchedObjects array from core data but there's are data in there.
What is the correct approach to do it?
EDIT 1:
The code above works only once if I'm trying to schedule it using NSTimer:
TrapService.mm:
self.managedObjectContext = appDelegate.managedObjectContext;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:kCORE_DATA_ALL_TRAPS_ENTITY inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSError *error = nil;
NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
NSLog(#"fetchedObjects.count: %d", fetchedObjects.count);
EDIT 2:
Another example of codes i'm using with core data, execute once, then all of the operations regarding core data doing nothing or giving me back array with 0 records.
TrapService.mm:
- (void)initializeQuadTree
{
self.qTree = [[QuadTree alloc] init];
self.qTree = [dbat addCoordinatesToQuadTree:self.qTree];
}
- (Traps*)getCloseTrapFromTree:(CLLocation*)location
{
return [dbat getCloseTrapFromTree:self.qTree andLocation:location];
}
DataBaseAllTraps.m:
- (QuadTree*)addCoordinatesToQuadTree:(QuadTree*)quadTree
{
if (quadTree == nil) {
quadTree = [[QuadTree alloc] init];
}
BOOL success = YES;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:kCORE_DATA_ALL_TRAPS_ENTITY inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSError *error = nil;
NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil || fetchedObjects.count == 0)
{
NSLog(#"addCoordinatesToQuadTree - localizedDescription: %#, userInfo: %#", error.localizedDescription, error.userInfo);
success = NO;
}
NSLog(#"addCoordinatesToQuadTree - fetchedObjects.count: %d", fetchedObjects.count);
if (success)
{
for (CoreDataAllTraps *trap in fetchedObjects)
{
double latitude = trap.lat.doubleValue;
double longitude = trap.lon.doubleValue;
double closePointLat = trap.close_point_lat.doubleValue;
double closePointLon = trap.close_point_lon.doubleValue;
DummyAnnotation *trapAnnotation = [[DummyAnnotation alloc] init];
if (closePointLat != 0.0 || closePointLon != 0.0) trapAnnotation.coordinate = CLLocationCoordinate2DMake(closePointLat, closePointLon);
else trapAnnotation.coordinate = CLLocationCoordinate2DMake(latitude, longitude);
[quadTree insertObject:trapAnnotation];
}
}
else
{
for (Traps *trap in kNETROADS_CONTEXT.arrayOfAllTraps)
{
double latitude = trap.lat;
double longitude = trap.lon;
double closePointLat = trap.closePointLat;
double closePointLon = trap.closePointLon;
DummyAnnotation *trapAnnotation = [[DummyAnnotation alloc] init];
if (closePointLat != 0.0 || closePointLon != 0.0) trapAnnotation.coordinate = CLLocationCoordinate2DMake(closePointLat, closePointLon);
else trapAnnotation.coordinate = CLLocationCoordinate2DMake(latitude, longitude);
[quadTree insertObject:trapAnnotation];
}
}
NSLog(#"TOTAL NUMBER OF TRAPS (%s): %i", __PRETTY_FUNCTION__, success?fetchedObjects.count:[Netroads sharedInstance].arrayOfAllTraps.count);
return quadTree;
}
- (Traps*)getCloseTrapFromTree:(QuadTree*)quadTree andLocation:(CLLocation*)location
{
NSLog(#"%s", __PRETTY_FUNCTION__);
NSArray *closeTraps = [quadTree neighboursForLocation:location.coordinate limitCount:1];
if (closeTraps.count == 0) { return nil; }
// NSAssert(closeTraps.count > 0, #"closeTraps.count == 0, get close trap from quad tree.");
int trapID = 0;
DummyAnnotation *trapLocation = closeTraps.firstObject;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:kCORE_DATA_ALL_TRAPS_ENTITY inManagedObjectContext:self.managedObjectContext];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%# == %f AND %# == %f", CLOSE_POINT_LAT, trapLocation.coordinate.latitude, CLOSE_POINT_LON, trapLocation.coordinate.longitude];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
[fetchRequest setFetchLimit:1];
NSError *error = nil;
NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects != nil && fetchedObjects.count > 0) { // We have close point
CoreDataAllTraps *trap = fetchedObjects.firstObject;
trapID = trap.trapID.intValue;
}
else { // We do not have close point, use normal coordinates (lat, lon)
NSLog(#"%s error: %#\n%#", __PRETTY_FUNCTION__, error.localizedDescription, error.userInfo);
fetchRequest = [[NSFetchRequest alloc] init];
entity = [NSEntityDescription entityForName:kCORE_DATA_ALL_TRAPS_ENTITY inManagedObjectContext:self.managedObjectContext];
predicate = [NSPredicate predicateWithFormat:#"%# == %f AND %# == %f", LAT, trapLocation.coordinate.latitude, LON, trapLocation.coordinate.longitude];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
[fetchRequest setFetchLimit:1];
error = nil;
fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects != nil && fetchedObjects.count > 0) {
CoreDataAllTraps *trap = fetchedObjects.firstObject;
trapID = trap.trapID.intValue;
}
else {
NSLog(#"%s error: %#\n%#", __PRETTY_FUNCTION__, error.localizedDescription, error.userInfo);
}
}
if (trapID > 0) {
return [self getTrap_trapID:trapID];
}
else {
return nil;
}
}
EDIT 3:
I'm creating a new MOC and still nothing, same problems:
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:appDelegate.persistentStoreCoordinator];

I did not analyze your code. (I'm too lazy. :-)) But when I did a search for a single save I found none.
Please remember, what is going on: In a standard set-up you have one SQL-DB as backend. You have different contexts for each thread/queue that (partially) takes out the data of the SQL-DB on a fetch request and (partially) saves it on a save request.
No context pushes its changes (including insertions and deletions) automatically to the DB or to another context. No context pulls changes pushed by another context automatically from the DB or another context. So transmitting data from context to another one has to be done a way "manually".
As long as you do not have deletions you can simply store the data when one context is done using save and listen to the did save notification on the other thread.

Read up on Apples documentation on how to use CoreData in a concurrent fashion.
Basically it is highly important to use separate NSManagedObjectContext per thread and not to pass objects between these threads, but only reference these by their NSManagedObjectID.
Your code example above needs more information on where you sue that code. But what makes me wonder immediately is
self.managedObjectContext = appDelegate.managedObjectContext;
If not run on main thread, this is exactly contrary to what the concurrency guide tells to do. With that line you only create a pointer that points to appDelegate.managedObjectContext. This is not a new object!
There is usually no need to synchronize or add locks and such, if done the right way.
To give a good answer, though your question is too vague and it would need a rather lengthy answer. But maybe after reading Apple's documentation you may be able to partially solve your problem and come back with questions on problems. that can be answered satisfactorily more easily.

Related

Core Data was mutated while being enumerated

I've an annoying problem with Core Data. My app need to get contacts from iPhone and save it in my database. I'm trying to do that in background thread.
I use above code for that:
[self performSelectorInBackground:#selector(fetchingContact) withObject:nil];
-(void)fetchingContact{
// Some Code
for (int i = 0; i < nPeople; i++)
{
//Some Code
NSManagedObjectContext *context = [APP_DELEGATE managedObjectContext];
ABRecordID recordID = ABRecordGetRecordID(person);
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Contact" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"(contactId = '%d')",recordID]];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
//crash
NSArray *contactObjArray = [context executeFetchRequest:fetchRequest error:&error];
//crash
if (error) {}
Contact *contacts;
if (contactObjArray.count == 0) {
contacts = [NSEntityDescription insertNewObjectForEntityForName:#"Contact" inManagedObjectContext:context];
}else {
contacts = [contactObjArray objectAtIndex:0];
}
//Some Code
}
}
In AppDelegate:
- (NSManagedObjectContext *)managedObjectContext
{
NSLog(#"managedObjectContext");
// Returns ;the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
return nil;
}
_managedObjectContext = [[NSManagedObjectContext alloc]initWithConcurrencyType: NSMainQueueConcurrencyType];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
return _managedObjectContext;
}
Here I try to save in my Core Data but it crash with error :
* Terminating app due to uncaught exception 'NSGenericException', reason: '* Collection <__NSCFSet: 0x15fad880> was mutated while being enumerated.'` at this line:
NSArray *contactObjArray = [context executeFetchRequest:fetchRequest error:&error];
I search already online, I found a lot of things but nothing helps me. When I run that, there is no place where Core Data is changed, or Contact entity. That does this error very strange.
If I run it in main thread I get no errors/ no crash, but if the app is quit in that time (while is executed) I lose all content from Contact
Please, any help. Tell me if I need to provide more information.
This error happen when you are modifying core data while you try to get them.
That also could be cause of the loop you're doing, you are inserting a new object in coredata without saving before you do an other retrieve. Try saving your managedobjectcontext :
favorite
I have an annoying problem with Core Data. My app need to get contacts from iPhone and save it in my database. I'am trying to do that in background thread.
I use above code for that:
[self performSelectorInBackground:#selector(fetchingContact) withObject:nil];
-(void)fetchingContact{
// Some Code
for (int i = 0; i < nPeople; i++)
{
//Some Code
NSManagedObjectContext *context = [APP_DELEGATE managedObjectContext];
ABRecordID recordID = ABRecordGetRecordID(person);
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Contact" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"(contactId = '%d')",recordID]];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
NSArray *contactObjArray = [context executeFetchRequest:fetchRequest error:&error];
if (error) {}
Contact *contacts;
if (contactObjArray.count == 0) {
contacts = [NSEntityDescription insertNewObjectForEntityForName:#"Contact" inManagedObjectContext:context];
[context save:&error];
}else {
contacts = [contactObjArray objectAtIndex:0];
}
//Some Code
}
}
If that doesn't solve your problem, maybe check if your method is called multiple times.

Core Data executeFetchRequest: not returning saved data

I have this method getLevelInformation that sends a fetch request to my Core Data store to acquire information about this int called _bestMoveCount based on two parameters, level and type of level (level being any int from 0-99, and type of level from 1-4). If the predicate hasn't been located, a new object is created for specific level and best move (and type of level if it hasn't been created already). Otherwise, I retrieve the best movement NSNumber and eventually convert it to my NSInteger instance var _bestMoveCount.
My problem: it appears that _levelInformation (by the time it checks to see if there is anything contained in the specific fetch) is always empty when I replay one of my levels (which it shouldn't, because I save the newly created data). When I go back to play the level again, and put a breakpoint at the if statement, I take a look at _levelInformation after the executeFetchRequest: method. Here's what I see:
<Score: 0x1cd68a40> (entity: Score; id: 0x1cd68470 <x-coredata:///Score/t510F3599-89E9-4A57-B9BC-34DE0131E2142> ; data: {
bestMovement = 0;
bestTime = 0;
level = 0;
overallScore = 0;
typeOfLevel = 0;
})
At this point, if I clicked on level 2, bestMovement should be something like 18, level should be 1, typeOfLevel should be 2 (I'm playing my second type). Any thoughts as to why this isn't saving my data?
- (void)getLevelInformation {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
_managedObjectContext = [(AppDelegate*)[[UIApplication sharedApplication]
delegate] managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Score" inManagedObjectContext:_managedObjectContext];
[fetchRequest setEntity:entityDescription];
NSNumber* levelNumber = [NSNumber numberWithInteger:_currentLevel];
NSNumber* typeNumber = [NSNumber numberWithInteger:_level];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K == %# AND %K == %#",
#"level", levelNumber, #"typeOfLevel", typeNumber];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
_levelInformation = [[_managedObjectContext
executeFetchRequest:fetchRequest error:&error] lastObject];
// the only way this fails is if the level the player is on has never been accessed
// OR RATHER, if the player never actually finished the level (thus never getting
// a best move count, best time, and overall score.
if (!_levelInformation) {
_levelInformation = [NSEntityDescription insertNewObjectForEntityForName:#"Score"
inManagedObjectContext:_managedObjectContext];
[_levelInformation setValue:levelNumber forKey:#"level"];
if (![_levelInformation valueForKey:#"typeOfLevel"]) {
[_levelInformation setValue:typeNumber forKey:#"typeOfLevel"];
}
[_levelInformation setValue:[NSNumber numberWithInteger:0] forKey:#"bestMovement"];
if (![_levelInformation.managedObjectContext save:&error]) {
NSLog(#"Unable to save managed object context.");
NSLog(#"%#, %#", error, error.localizedDescription);
}
}
_levelInformation.bestMovement = [_levelInformation valueForKey:#"bestMovement"];
_bestMoveCount = [_levelInformation.bestMovement integerValue];
}
Forgot to mention. After the level is completed I save the new data again.
- (void)saveTheBestMove {
[_levelInformation setValue:[NSNumber numberWithInteger:_bestMoveCount] forKey:#"bestMovement"];
NSError *error;
[_managedObjectContext save:&error];
}

Core-Data executeFetchRequest freezes App in a background thread

I have a saveMOC which is the direct parent of a mainMOC, and I need for online fetch another tmpMOC in order not to block my UI whilst fetching a ton of records from the Internet.
My app freezes.
I could narrow it to this very point:
fetchedItems = [tmpMOC executeFetchRequest:fetchRequest error:&error];
I tried to enclose this within dispatch_sync, [moc.parentcontext.parentcontext.persistentStoreCoordinator lock/unlock], [whatevermoc performBlockAndWait]...
I also try to fetch the _mainMOC... No way...
I understand that executeFetchRequestis not thread safe, so, how do I lock whatever I need to lock to get sure I am not inserting a double?
Anybody could help?
UPDATE (1)
_saveMOC instantiation in AppDelegate:
- (NSManagedObjectContext *)managedObjectContext
{
if (_mainMOC != nil) {
return _mainMOC;
}
if (_saveMOC == nil) {
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_saveMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_saveMOC setPersistentStoreCoordinator:coordinator];
[_saveMOC setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
}
}
if (_mainMOC == nil) {
_mainMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_mainMOC setParentContext:_saveMOC];
[_mainMOC setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(saveContextChanges:) name:NSManagedObjectContextDidSaveNotification object:_mainMOC];
temporaryMOCcreation in MainViewController:
NSManagedObjectContext *temporaryContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
temporaryContext.parentContext = _mainMOC;
[temporaryContext performBlock:^{ //AndWait
NSError *error=nil;
Feed *feed=(Feed *)[temporaryContext existingObjectWithID:feedID error:&error];
//...
[RSSParser parseRSSFeedForRequest:req success:^(NSArray *feedItems)
{
NSLog(#"[MasterViewController::fetchPosts] inserting %d Posts.../OK", (int)[feedItems count]);
feed.valid = [NSNumber numberWithBool:YES];
for(RSSItem *i in feedItems)
{
[self createPostInFeed:feed withTitle:i.title withContent:(i.content?i.content:i.itemDescription) withURL:[i.link absoluteString] withDate:(i.pubDate?i.pubDate:[NSDate date]) inMOC:temporaryContext];
}
[[NSNotificationCenter defaultCenter] postNotificationName:#"NewPostsFetched" object:f];
Then the freeze happens in createPostInFeed:
// #synchronized(fetchRequest) { // THIS DOESN'T CHANGE ANYTHING
// [moc.persistentStoreCoordinator lock]; // THIS NEITHER
NSArray *fetchedItems;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *ent = [NSEntityDescription entityForName:#"Post" inManagedObjectContext:moc];
fetchRequest.entity = ent;
fetchRequest.propertiesToFetch = [NSArray arrayWithObjects:#"title", #"url", #"feed.name", nil];
[fetchRequest setResultType:NSDictionaryResultType];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"title == %# AND url == %# AND feed.rss == %#", title, url, feed.rss];
NSError *error = nil;
fetchedItems = [moc executeFetchRequest:fetchRequest error:&error]; // FREEZES HERE
// [moc.persistentStoreCoordinator unlock]; // THIS DOESN'T CHANGE ANYTHING
// } // Synchronized neither...
Update (2):
Here's what the Time Profiler sees.
Update (3):
Block edited:
NSUInteger fetchedItems;
NSString *feedRSS=feed.rss;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *ent = [NSEntityDescription entityForName:#"Post" inManagedObjectContext:moc];
fetchRequest.entity = ent;
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"title == %# AND url == %# AND feed.rss == %#", title, url, feedRSS];
NSLog(#"MainViewController::createPostInFeed> Before fetchRequest for %# (%#)", title, url);
NSError *error = nil;
fetchedItems = [moc countForFetchRequest:fetchRequest error:&error];
NSLog(#"MainViewController::createPostInFeed> After fetchRequest for %# (%#)", title, url); // THIS NSLOGGING NEVER HAPPENS
Update (4):
New profiler pic with call tree inverted.
What is your goal with createPostInFeed? You show that you are doing a fetch but what do you do with that fetch? Is this a "insert or update" check? or is it just a "insert or skip" test?
Any fetch is going to lock the NSPersistentStoreCoordinator and cause your application to potentially lock up while the fetch is being performed. There are ways to mitigate that:
Run Instruments
Make your fetches more efficient
If you don't need objects (in a insert or skip test) then do a count instead
Fetch on a background queue and make sure your main MOC has all the objects it needs to avoid a lock
What does your instruments profile show you?
What does createPostInFeed do with the results of that fetch?

array count returned from NSFetchRequest is wrong

I'm using Core Data to cache data, here is my code to insert object:
//data array count is 15
for (NSDictionary *dictionary in dataArray)
{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"CacheData" inManagedObjectContext:context];
[request setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"title LIKE '%#'",dictionary[#"title"]];
[request setPredicate:predicate];
NSArray *fetchArray = [context executeFetchRequest:request error:NULL];
if ([fetchArray count] == 0)
{
CacheData *cacheData = [NSEntityDescription insertNewObjectForEntityForName:#"CacheData" inManagedObjectContext:context];
[cacheData setTitle:dictionary[#"title"]];
[cacheData setLink:dictionary[#"link"]];
[cacheData setPublishDate:dictionary[#"pubDate"]];
NSError *insertError = nil;
if (![context save:&insertError])
{
return NO;
}
}
}
The count of dataArray is 15, so I should insert 15 items to Core Data.
But once I used NSFetchRequest to fetch items, the array count returned added 1, and become 16, and fetch items again, it added 1 to 17 again:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"CacheData" inManagedObjectContext:[[CacheDataManagement sharedInstance] managedObjectContext]];
[request setEntity:entity];
NSArray *fetchArray = [[[CacheDataManagement sharedInstance] managedObjectContext] executeFetchRequest:request error:NULL];
for (CacheData *data in fetchArray) {
NSLog(#"fetch:%#",[data title]);
}
NSLog(#"%ld",[fetchArray count]); //fetch array count is 16
Something wrong with my code ?
Update
Changed if ([fetchArray count] != 0) { … } to if ([fetchArray count] == 0) { … }
Since the problem it's not quite clear to me (I would like to have more details), I'll try to give you some hints.
First, the save should be done outside the for loop (In addition it's not correct to do a return within it).
// for in loop here
NSError *insertError = nil;
if (![context save:&insertError]) {
NSLog("%#", insertError);
return NO; // do it if the method you are running in returns a bool
}
Second, to check for duplicated you should rely on a GUID but if don't have it the predicate should look like the following.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"title == %#",dictionary[#"title"]];
Third, also the execute for executeFetchRequest:error: should be replace with countForFetchRequest:error: since you don't need to return the objects but only a count. According to Apple doc, it
Returns the number of objects a given fetch request would have
returned if it had been passed to executeFetchRequest:error:.
Finally, in the for loop you are executing a request each time. My suggestion is to move execute a request before the loop and then checking for results within it. This according to Implementing Find-or-Create Efficiently. In this case, the pattern will enforce you to have a GUID for you entity.
Obviously, these are just hints. The real way to find the problem is to debug. In addition, I will perform tests starting from a fresh environment, i.e. the app has been deleted from the simulator or device.
In your code [fetchArray count] != 0 checks the inserting data is already exists. Try changing it like
if ([fetchArray count] == 0)
{
CacheData *cacheData = [NSEntityDescription insertNewObjectForEntityForName:#"CacheData" inManagedObjectContext:context];
[cacheData setTitle:dictionary[#"title"]];
[cacheData setLink:dictionary[#"link"]];
[cacheData setPublishDate:dictionary[#"pubDate"]];
NSError *insertError = nil;
if (![context save:&insertError])
{
return NO;
}
}

How to update in Core Data?

I saw many questions about Core Data updates. Actually I am creating a simple application contact list app. It contains add, edit, delete and update functionalities. Here my update code. It works and updates, but it updates all the contact list. I need to update specific contacts only.
- (IBAction)updatePressed:(id)sender
{
delegate = [[AppDelegate alloc]init];
delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
name2 = emailtxt1.text;
email2 = nametext1.text;
mobile2 = numbertxt1.text;
dict = [[NSMutableDictionary alloc] init];
[dict setObject:nametext1.text forKey:#"NAME"];
[dict setObject:emailtxt1.text forKey:#"EMAIL"];
[dict setObject:numbertxt1.text forKey:#"MOBILE"];
[delegate UpdateDiary:dict];
}
- (void)UpdateDiary:(NSMutableDictionary *)dictionary
{
NSLog(#"update book Details Function Entered");
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Diary"inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSArray *mutableFetchResult = [[[self.managedObjectContext executeFetchRequest:fetchRequest error:&error] mutableCopy] autorelease];
if (mutableFetchResult == nil) {
NSLog(#"Fetch result error %#, %#", error, [error userInfo]);
}
for (Diary *ob2 in mutableFetchResult)
{
{
ob2.name = [dictionary objectForKey:#"NAME"];
ob2.email=[dictionary objectForKey:#"EMAIL"];
ob2.phone=[dictionary objectForKey:#"MOBILE"];
}
}
if(![self.managedObjectContext save:&error])
{
if([error localizedDescription] != nil)
{
NSLog(#"%#",error);
}
else
{
}
}
}
You need to set a predicate on your fetch request. That's how it knows which object(s) you want, rather than just fetching them all.
You could do something like:
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"email == %#", anEmailAddress];
If you did that, then the result of executing the fetch request would just be objects that matched the email address you set in the predicate.
Note, of course, that if there is more than one object with the same email address, then the fetch request would fetch all of them.
A better design for your app might be, when you go into the edit form, keep around the Core Data object that you're editing, possibly in a property on your view controller. (You'll have it around at that point I reckon, since you'll need to know what to populate the fields with.) That way you don't need to perform a fetch at the time the user is trying to commit the edit — you can just use the object you've kept around.
- (void)UpdateBook:(NSMutableDictionary *)dictionary
{
NSLog(#"update book Details Function Entered");
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Book"inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"bookID = %#", [dictionary objectForKey:#"vID"]];
NSArray *mutableFetchResult = [[[self.managedObjectContext executeFetchRequest:fetchRequest error:&error] mutableCopy] autorelease];
if (mutableFetchResult == nil) {
NSLog(#"Fetch result error %#, %#", error, [error userInfo]);
}
for (Book *ob2 in mutableFetchResult)
{
{
ob2.name = [dictionary objectForKey:#"VName1"];
ob2.author=[dictionary objectForKey:#"VAuthor1"];
ob2.discription=[dictionary objectForKey:#"VDiscription1"];
ob2.bookID=[dictionary objectForKey:#"vID"];
}
}
if(![self.managedObjectContext save:&error])
{
if([error localizedDescription] != nil)
{
NSLog(#"%#",error);
}
else
{
}
}
}

Resources