NSMergeConflict with newVersion = <deleted> - ios

After spending a lot of hours with this error I come here to ask if somebody have some information about this error. I load two entities, a Parks entity and a GuidedTour entity. A Paks can be relatet to many GuidedTours but when I try to save it, the error raises:
Error Domain=NSCocoaErrorDomain Code=133020 "(null)" UserInfo={conflictList=(
"NSMergeConflict (0x17026afc0) for NSManagedObject (0x1740d94b0) with objectID '0xd000000000240002 ' with oldVersion = 0 and newVersion = and old cached row = {\n language = \"de_DE\";\n text = \"Apapapapapa"...}
I can't understand what is wrong, my Merge policy is: NSMergeByPropertyStoreTrumpMergePolicyType
And my code:
AppDelegate appDelegate = (AppDelegate)[[UIApplication sharedApplication] delegate];
self.context = [appDelegate managedObjectContext];
NSError *error = nil;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"GuidedTours" inManagedObjectContext:self.context ];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [self.context executeFetchRequest:fetchRequest error:&error];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"guided_tours_parks == NULL"];
fetchedObjects = [fetchedObjects filteredArrayUsingPredicate:predicate];
GuidedTours *tour = [fetchedObjects firstObject];
fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entityPark = [NSEntityDescription entityForName:#"Parks" inManagedObjectContext:self.context ];
[fetchRequest setEntity:entityPark];
fetchedObjects = [self.context executeFetchRequest:fetchRequest error:&error];
predicate = [NSPredicate predicateWithFormat:#"name == %#",[jsonData objectForKey:#"Park ID"] ];
fetchedObjects = [fetchedObjects filteredArrayUsingPredicate:predicate];
Parks *park = [fetchedObjects firstObject];
[tour setGuided_tours_parks:park];
[park addParks_guided_toursObject:tour];
// Save the object to persistent store
if (![self.context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
Does somebody have any idea?
Thank you!

I had this exact same error, and in my case it occurred on inserts after attempting to perform an NSBatchDeleteRequest. The problem was that after performing the NSBatchDeleteRequest, I was not calling the [self.context reset] method.
As per a WWDC 2015 talk, batch updates/deletions circumvent the context and directly modify the persistent store file. Thus, if you do not call reset, your context will contain deleted objects which can cause the above merge conflict you experienced.
https://developer.apple.com/videos/play/wwdc2015/220/

Related

NSPredicate to check property of first 5 objects in a Core Data Relationship

I am looking to create a predicate that can check the TypeID of the first 5 objects in a Core Data relationship.
Here is what I am trying, but it doesn't work:
int num = 5;
NSMutableArray *predicates = [[NSMutableArray alloc] init];
for (int i = 0; i < num; i++) {
[predicates addObject:[NSPredicate predicateWithFormat:#"SELF IN %# AND logs[%i].TypeID == 3", items, i]];
}
This gives the error:
error: SQLCore dispatchRequest: exception handling request:
< NSSQLFetchRequestContext: 0x281837aa0 > , Unsupported function
expression logs[0].TypeID with userInfo of (null) CoreData:
error: SQLCore dispatchRequest: exception handling request:
< NSSQLFetchRequestContext: 0x281837aa0 > , Unsupported function
expression logs[0].TypeID with userInfo of (null)
I realize I am probably doing this wrong, so is there a different way that I could be doing this using NSPredicate?
if you want only fetch 5. Set fetchLimit to 5. Maybe the following code can not run right away, but the principle is same. You can add a property or get function to assign the "logs.TypeID" like -(Int)myID {return logs[0].TypeID;} then "SELF IN %# AND myID == 3" would solve the problem.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Entity name" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF IN %# AND logs.TypeID == 3", items];
[fetchRequest setPredicate:predicate];
fetchRequest.fetchLimit = 5;
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
}
I’m not sure I understand the questions, but I don’t think it’s possible to do what you asked, at least not as worded. It doesn’t seem like it would be possible to inspect only five objects in the relationship and stop there, returning any number of matches or none at all, even if there are more in the database.
However, and I think this may be what you were getting at, it is possible to find five objects that are both in the items set and have at least one log with a typeID == 3. It can be done similar to what E.Coms proposed, except you need a subquery to handle the relationship. (I am assuming logs is a to-many relationship, either one-to-many or many-to-many).
Note that the following code has not been tested:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Entity name" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF IN %# AND SUBQUERY(logs, $log, $log.typeID == 3).#count != 0", items];
[fetchRequest setPredicate:predicate];
fetchRequest.fetchLimit = 5;
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
}

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.

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!

Core data - NSFetchrequest fetching twice the actual count of data

NSFetchRequest in the below code is fetching in multiples times count of the data.. Example I have 3 as array count 1st time the method is called, next time it become 6 next 9 and so on. But in the database, the count remains as 3. Why is this happening?
// the method is called in viewDidLoad
- (void) create {
[orderList removeAllObjects];
NSError * error;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"OrderList"
inManagedObjectContext:context];
for (int addOrd = 0; addOrd < orderMainArray.count; addOrd++) {
GetOrders *getOrd = (GetOrders*)[NSEntityDescription insertNewObjectForEntityForName:#"GetOrders" inManagedObjectContext:context];
OrderList *ordList = (OrderList*)[NSEntityDescription insertNewObjectForEntityForName:#"OrderList" inManagedObjectContext:context];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"orderId contains[cd] %#", [[orderMainArray objectAtIndex:addOrd] objectForKey:#"orderId"]];
[fetchRequest setPredicate:predicate];
[fetchRequest setEntity:entity];
NSLog(#"\n \n \n count for same data = %d", [context countForFetchRequest:fetchRequest error:&error]);
if ([context countForFetchRequest:fetchRequest error:&error] ==0 ) {
// all data is saved here
[getOrd addOrderListObject:ordList];
if ([context save:&error]) {
NSLog(#"The save was successful! %#", ordList.orderId);
}
else {
NSLog(#"The save wasn't successful: %#", [error userInfo]);
}
}
}
NSFetchRequest *fetchRequest1 = [[NSFetchRequest alloc] init];
[fetchRequest1 setEntity:entity];
orderList = [[context executeFetchRequest:fetchRequest1 error:&error] mutableCopy]; // array count increases every time the method is called
}
Each time I call insertNewObjectForEntityForName:InManagedObjectContext: I was creating and adding new objects. So changed that and now works fine.
The code mixes fetch requests and inserts of new managed objects.
You insert one OrderList NSManagedObject and one GetOrder NSManagedObject in each iteration of the for-loop. Given your description, I would assume that orderMainArray.count has a value of 3.

Having only one instance of an entity in Core Data

I am making an application where you need to log in with a 4 digit password but there can only be one password at a time. I am trying to save it to core data but whenever the user adds a new password it just adds it to the long list. How can I restrict an entity to only have one instance of itself?
Here is my code just in case it will help:
-(BOOL)savePassword:(NSString*)password{
AppDelegate * appDelegate = [[AppDelegate alloc]init];
NSManagedObjectContext * context = [appDelegate managedObjectContext];
AppData * appData = (AppData*)[NSEntityDescription insertNewObjectForEntityForName:#"AppData" inManagedObjectContext:context];
appData.password = password;
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"AppData" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
NSLog(#"There was an error:%#",error);
}
for (AppData * adata in fetchedObjects) {
NSLog(#"Password:%#",adata.password);
}
return YES;
}
Thanks!
The right approach here is to not put this data in Core Data. If you only have one instance, there's no point in using Core Data to solve the problem. There's no benefit to using Core Data for this. Put it somewhere else. Code solutions miss the point, because even if it works, it's a bad design.
You should do like this, first create fetch request and execute a fetch. check if object exist, update data. else if no data exist create an object and save it.
If name of entity which is storing password.
Your code should look like this
AppData * appData;
NSManagedObjectContext * context = [appDelegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"AppData" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if(fetchObjects.count > 0){
appData = [fetchObjects objectAtIndex:0];//assume there will be one object
// and do reset of thing
}
else{
appData = (AppData*)[NSEntityDescription insertNewObjectForEntityForName:#"AppData" inManagedObjectContext:context];
}
appData.password = password;
// save moc here
[context save:nil];

Resources