How to update in Core Data? - ios

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

Related

Update a Core Data record in iOS application

I've just read some tutorials and decide to add Core Data storage to my project. Then I implement "create" and "read" methods. It works OK.
But then I encountered a problem with "update" method.
- (void)updateForecastPlace:(NSString *)placeString
{
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:ENTITY_NAME inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
WFForecast *forecastToUpdate;
for (WFForecast *forecast in fetchedObjects)
{
if ([[forecastToUpdate timestamp] compare:[forecast timestamp]] == NSOrderedAscending)
{
forecastToUpdate = forecast;
}
}
[forecastToUpdate setPlace:placeString];
error = nil;
if ([context save:&error])
{
NSLog(#"Forecast information was updated!");
}
else
{
NSLog(#"The forecast information was not updated: %#", [error userInfo]);
}
}
I'm fetching objects from context. (It's OK)
Then choose one to update.
Setup new value to its property( [forecastToUpdate setPlace:placeString];)
Then save the context. ( [context save:&error] )
It seems like it works (it's rise no errors and send success massage to console log). But when I read this object it appears to be non-updated.
I read a lot of stuff on this problem but didn't figure out how to fix it.
Any suggestions, please?
UPDATE: I check the value of my updated object property place
[forecastToUpdate setPlace:placeString];
NSLog(#"---arg value %#", placeString);
NSLog(#"---updated value %#", [forecastToUpdate place]);
and got the output like:
---arg value Sydney, Australia
---updated value (null)
Any idea what caused such mistake?
Unfortunately the problem was in my inattentiveness :(
I forgot to assign fetched object with my objectToUpdate pointer before compare values and do other stuff.
WFForecast *lastestForecast = fetchedObjects[0]; // <- missed this row
for (WFForecast *forecast in fetchedObjects)
{
NSLog(#"%#", [forecast place]);
if ([[lastestForecast timestamp] compare:[forecast timestamp]] == NSOrderedAscending)
{
lastestForecast = forecast;
}
}

Saving coredata on background thread causes fetching into a deadlock and crash

I'm saving coredata in a background method (parent-child) but fetching is done on main thread. So i'm getting a deadlock in the fetch method and sometimes app crashed. Is there anything wrong i'm doing? How can i improve both save and fetch without affect main thread? I have read many documents but none of them explaining me how to use both in a project. If this is a wrong question please guide me to proper solution and let me know my mistakes please.
-(ThreadInfo *)retrieveSolicitationInfoForThreadID:(NSString*)inThreadID;
{
NSString *loginUser=[[NSUserDefaults standardUserDefaults] valueForKey:#"currentUser"];
AppDelegate *sharedDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [sharedDelegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ThreadInfo"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *userPredicate = [NSPredicate predicateWithFormat:#"userEmail == %#",loginUser];
NSPredicate *threadPredicate = [NSPredicate predicateWithFormat:#"threadID == %#",inThreadID];
NSPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates: #[userPredicate, threadPredicate]];
[fetchRequest setPredicate:compoundPredicate];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:nil];
if(fetchedObjects.count!=0)
{
ThreadInfo *threadInfo=[fetchedObjects objectAtIndex:0];
return threadInfo;
}
return nil;
}
SAVE
-(void)updateThreadEntityWithSyncDetails:(NSMutableDictionary *)inDictionary
{
NSString *loginUser=[[NSUserDefaults standardUserDefaults] valueForKey:#"currentUser"];
AppDelegate *sharedDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [sharedDelegate managedObjectContext];
// NSManagedObjectContext *writerContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
// [writerContext setPersistentStoreCoordinator:[sharedDelegate persistentStoreCoordinator]];
// create main thread MOC
context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
context.parentContext = writerContext;
NSManagedObjectContext *contextforThread = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
contextforThread.parentContext = context;
[contextforThread performBlock:^{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ThreadInfo"
inManagedObjectContext:contextforThread];
[fetchRequest setEntity:entity];
NSPredicate *userPredicate = [NSPredicate predicateWithFormat:#"userEmail == %#",loginUser];
NSPredicate *threadPredicate = [NSPredicate predicateWithFormat:#"threadID == %#",[inDictionary valueForKey:#"thread"]];
NSPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates: #[userPredicate, threadPredicate]];
[fetchRequest setPredicate:compoundPredicate];
NSArray *fetchedObjects = [contextforThread executeFetchRequest:fetchRequest error:nil];
for (ThreadInfo *threadInfo in fetchedObjects)
{
if([[inDictionary allKeys] containsObject:#"userEmail"])
{
if([inDictionary valueForKey:#"userEmail"]!=[NSNull null])
{
threadInfo.userEmail=[inDictionary valueForKey:#"userEmail"];
}
}
if([[inDictionary allKeys] containsObject:#"badgeValue"])
{
if([inDictionary valueForKey:#"badgeValue"]!=[NSNull null])
{
threadInfo.badgeValue=[inDictionary valueForKey:#"badgeValue"];
}
}
if([[inDictionary allKeys] containsObject:#"choice4Percentage"])
{
if([inDictionary valueForKey:#"choice4Percentage"]!=[NSNull null])
{
threadInfo.choice4Percentage=[inDictionary valueForKey:#"choice4Percentage"];
}
}
if([[inDictionary allKeys] containsObject:#"choice5Percentage"])
{
if([inDictionary valueForKey:#"choice5Percentage"]!=[NSNull null])
{
threadInfo.choice5Percentage=[inDictionary valueForKey:#"choice5Percentage"];
}
}
}
NSError *error;
if(![context save:&error]) {
NSLog(#"Child error : %#",error);
}
[context performBlock:^{
NSError *error;
if(![context save:&error]) {
NSLog(#"%#",error);
}
}];
}];
}
The issue is your fetch. You are fetching two strings against what I am guessing is a large number of entities in your store. Your fetch performance is 100% of your issue.
A) You should not be doing string compares if it can be avoided
B) You should not be doing TWO string compares ever
Your data model needs to be refactored. Doing two string compares against a table that has a large number of entities is going to perform badly no matter what.
Update
Your data structure is bad. I don't know how many ways I can say that. Ideally you want an integer or other number structure as your unique identifier.
Since you are not telling me/us how many entities you are dealing with I can only guess you have a large number. How man results are expected? One? Or Many? Is this a unique constraint that you are working against? If not, why isn't there a unique constraint coming from your server? If it is a unique constraint why haven't you limited the fetch size like I suggested?
You are not giving me any information but hoping I can magically fix your problem.
Reducing it to one string is something you can try but you are asking me how your data is going to behave. TEST it. You have instruments and you have the data. Change things and compare the results.
Strings are expensive. They always have been and always will be.
Update
Since it is a unique constraint you can do yourself a large favor and change your code just a bit:
- (ThreadInfo *)retrieveSolicitationInfoForThreadID:(NSString*)inThreadID;
{
NSString *loginUser = [[NSUserDefaults standardUserDefaults] valueForKey:#"currentUser"];
AppDelegate *sharedDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [sharedDelegate managedObjectContext];
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"ThreadInfo"];
[fetchRequest setReturnsObjectsAsFaults:NO];
//Since there is every only one tell SQL to stop looking after one
[fetchRequest setFetchBatchSize:1];
/* Change these to your single constraint
NSPredicate *userPredicate = [NSPredicate predicateWithFormat:#"userEmail == %#",loginUser];
NSPredicate *threadPredicate = [NSPredicate predicateWithFormat:#"threadID == %#",inThreadID];
NSPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates: #[userPredicate, threadPredicate]];
[fetchRequest setPredicate:compoundPredicate];
*/
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
NSLog(#"Error fetching: %#\n%#", [error localizedDescription], [error userInfo]);
abort();
}
return [fetchedObjects lastObject];
}
Setting the fetch limit will cause the store to stop looking after the first match which will reduce the fetch time. Moving it to a single string also reduces the fetch time.
In the future, avoid strings in this situation. Doing an integer primary key is incredibly fast and will avoid this performance problem entirely.
facing to problems such as coreData and multiplyThreads,you should add the following code in the corresponding method:
[[NSNotificationCenter defaultCenter] addObserver: cdm.managedObjectContext selector: #selector(mergeChangesFromContextDidSaveNotification:) name:NSManagedObjectContextDidSaveNotification object: nil];
Then the coreData changes at a thread,it will instantly update it in another thread where the coreData is being used.
Wish the answer will help you!

IOS Core Data - Find or Create duplicates inserts

I'm missing something in my logic when trying to sync web service data with local store and I need your help. This is what I've got:
I have one NSArray of NSDictionaries describing each event object (downloaded from web), which I sort by event id. Then I fetch local store using IN predicate and also sort it by event id. Then I try to iterate and match the ids and if they match, i update record and if they don't match i create new NSManagedObject. It works fine if the newly downloaded event object has a greater eventID than last eventID in local store, but if the eventID from web service is smaller than the one in local store then it INSERTS ALL OBJECTS, no matter if they exist or not and that exactly is my problem.
So in other words, if a new record is at the beginning of sorted array it will add every object, but if it is at the end of sorted array it will update all except the new one. I need it to create the new one and update old ones.
Here's some code:
The function with the logic where I believe I'm missing something:
- (void)findOrCreateObject:(NSArray*)eventArray
{
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
//get sorted stored records
NSArray *fetchedRecords = [self.fetchedResultsController fetchedObjects];
//sort dictionaries
NSSortDescriptor *aSortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"id" ascending:YES];
NSArray *downloadedRecords = [self.events sortedArrayUsingDescriptors:[NSArray arrayWithObject:aSortDescriptor]];
NSLog(#"DOWNLOADED EVENTS = %#", downloadedRecords);
NSLog(#"FETCHED EVENTS = %#", fetchedRecords);
//if store is not empty we need to walk through data and add/update records, otherwise/ELSE we need to import initial data
if (fetchedRecords.count != 0) {
//stores has records already
NSLog(#"FIND OR CREATE PROCESS");
if ([downloadedRecords count] > 0) {
NSArray *storedRecords = [self fetchEvents:eventArray withContext:context];
NSUInteger currentIndex = 0;
for (NSDictionary* event in downloadedRecords) {
Event* eventObject = nil;
if ([storedRecords count] > currentIndex) {
eventObject = [storedRecords objectAtIndex:currentIndex];
}
NSLog(#"STRING VALUE OF KEY = %#", [[eventObject valueForKey:#"eventID"]stringValue]);
if ([[event valueForKey:#"id"] isEqualToString:[[eventObject valueForKey:#"eventID"] stringValue]]) {
//Update Record
NSLog(#"Updating Record!!!");
[self updateManagedObject:eventObject withRecord:event inContext:context];
}
else
{
//New Record
NSLog(#"Inserting Record!!!");
eventObject = (Event*)[NSEntityDescription insertNewObjectForEntityForName:#"Event" inManagedObjectContext:context];
eventObject.eventID = [self makeNumberFromString:[event valueForKey:#"id"]];
eventObject.title = [event valueForKey:#"title"];
eventObject.venue = [event valueForKey:#"venue"];
}
currentIndex++;
}
}
}
else
{
//import initial data
NSLog(#"IMPORTING INITIAL DATA");
for (NSDictionary* event in downloadedRecords) {
Event *eventObject = (Event*)[NSEntityDescription insertNewObjectForEntityForName:#"Event" inManagedObjectContext:context];
eventObject.eventID = [self makeNumberFromString:[event valueForKey:#"id"]];
eventObject.title = [event valueForKey:#"title"];
eventObject.venue = [event valueForKey:#"venue"];
}
}
// Save the context.
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
}
The FETCHEVENTS method:
-(NSArray*)fetchEvents:(NSArray*)eIDs withContext:(NSManagedObjectContext*)context
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Event" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(eventID IN %#)", eIDs];
[fetchRequest setPredicate:predicate];
[fetchRequest setSortDescriptors:#[ [[NSSortDescriptor alloc] initWithKey: #"eventID" ascending:YES] ]];
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
NSLog(#"No rows returned");
}
return fetchedObjects;
}
The Update Object method:
- (void)updateManagedObject:(NSManagedObject*)object withRecord:(NSDictionary*)record inContext:(NSManagedObjectContext*)context
{
[object setValue:[self makeNumberFromString:[record valueForKey:#"id"]] forKey:#"eventID"];
[object setValue:[record valueForKey:#"title"] forKey:#"title"];
[object setValue:[record valueForKey:#"venue"] forKey:#"venue"];
}
I'm calling findOrCreate method once I download the web service data and parse it.
Let me know if you have any other questions.
Try this,
- (void)findOrCreateObject:(NSArray*)eventArray {
//if store is not empty we need to walk through data and add/update records, otherwise/ELSE we need to import initial data
if (fetchedRecords.count != 0) {
//stores has records already
NSLog(#"FIND OR CREATE PROCESS");
if ([downloadedRecords count] > 0) {
NSArray *storedRecords = [self fetchEvents:eventArray withContext:context];
for (NSDictionary* event in downloadedRecords) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"eventID = %#",[event valueForKey:#"id"]];
NSArray *matchedArray = [storedRecords filteredArrayUsing
Predicate:predicate];
Event* eventObject = nil;
if ([matchedArray count] > 0) {
//Update Record
NSLog(#"Updating Record!!!");
eventObject = [matchedArray objectAtIndex:0];
[self updateManagedObject:eventObject withRecord:event inContext:context];
}
else
{
//New Record
NSLog(#"Inserting Record!!!");
eventObject = (Event*)[NSEntityDescription insertNewObjectForEntityForName:#"Event" inManagedObjectContext:context];
eventObject.eventID = [self makeNumberFromString:[event valueForKey:#"id"]];
eventObject.title = [event valueForKey:#"title"];
eventObject.venue = [event valueForKey:#"venue"];
}
}
}
} else {
.....
}
}
I think, every time you insert a new event object, you should update storedObjects such that it should now contain the inserted object.
Or more simply, you should put the initialisation line of storedObjects inside your for loop. (This would make sure that as you enumerate from the beginning of downloadedObjects every eventObject will have the same index on it as on storedObjects. But, with regards to your code this will only be true if all elements of storedObjects will always be found in downloadedObjects which, I assume is the case.)
One thing though, isn't fetchedRecords supposed to be the same as storedObjects, if they are you should just reassign storedObjects as [self.fetchedResultsController fetchedObjects], as it would reflect the changes in your context without executing another fetch request which would solve the inefficiency of the suggestion above.

Async core data from multiple class and threads

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.

Not able to handle executeFetchRequest error

My fetch request works fine and I get my fetched objects without any problems. What I want to do, is handle the error in case the entity doesn't exist. The problem is, I can't handle the error because the app crashes when I call executeFetechRequest: error: without any warnings.
My fetch looks like:
NSManagedObjectContext *context = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = [NSEntityDescription entityForName:#"Info" inManagedObjectContext:context];
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"infoID" ascending:YES]];
[request setReturnsObjectsAsFaults:NO];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"infoID like %#",[a substringFromIndex:13]];
request.predicate = predicate;
request.fetchBatchSize = 1;
NSError *error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
if (error == nil) {
...
}
else {
//handle error
}
As I said, there's no problem as long as the entity exists, but I want to handle the error if it doesn't exist. Any idea? Cheers
You could ask the model if such entity is present:
NSArray *entities = managedObjectModel.entities;
BOOL canExecute=NO;
for(NSEntityDescription *ed in entities) {
// check if entity name is equal to the one you are looking for
if(found) {
canExecute=YES;
break;
}
}
if(canExecute) {
// execute your request and all the rest...
} else {
NSLog(#"Entity description not found");
}
if doesn't exist you don't execute the fetch reuest

Resources