Core data - Collection __NSCFSet was mutated while being enumerated - ios

Application crashes while inserting the data into core data.
There are more then 500 records going to insert into the database. Initially check condition if messageId not exit in database then insert the record in database.
But application crash on
BOOL isExist = [context countForFetchRequest:request error:&error];
with the error Collection <__NSCFSet: 0x16f601c0> was mutated while being enumerated.'
appDelegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
NSManagedObjectContext *context = appDelegate.managedObjectContext;
__block NSManagedObjectContext *managedObjectContext = appDelegate.managedObjectContext;
__block NSManagedObjectContext *writerObjectContext = appDelegate.writerManagedObjectContext;
__block NSManagedObjectContext *temporaryContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
temporaryContext.parentContext = managedObjectContext;
[temporaryContext performBlockAndWait:^{
NSError *error;
for (StreamData *stream in streamData) {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:DB_TABLE_STREAM];
[request setPredicate:[NSPredicate predicateWithFormat:#"stream_id = %#", stream.messageId]];
[request setFetchLimit:1];
BOOL isExist = [context countForFetchRequest:request error:&error];
if (!isExist) {
CF_Stream *object = [NSEntityDescription insertNewObjectForEntityForName:DB_TABLE_STREAM inManagedObjectContext:context];
[object setStream_id:[NSNumber numberWithLongLong:[stream.messageId longLongValue]]];
[object setType:stream.streamType];
[object setTime_stamp:[[DateTimeManager sharedInstances] getStreamDate:stream.streamTime]];
}
if (![temporaryContext save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
if (![temporaryContext save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
[managedObjectContext performBlock:^{
NSError *error = nil;
if (![managedObjectContext save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
[writerObjectContext performBlock:^{
NSError *error = nil;
if (![writerObjectContext save:&error]) {
completionBlock(NO);
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
} else {
completionBlock(YES);
}
}];
}];
}];

Related

IOS/Objective-C: Background save to core data after NSFetchedResultsController

After performing a sort using NSSortDescriptor on various attributes, I would like to capture the sort order and save it back to core data. To keep the uX steady, I am trying to do this in the background using a separate MOC. However, I'm getting a mutated while enumerated error, possibly because several other things are going on--including syncing with a server and the FRC doing a fetch.
Can anyone see what might be wrong with my code? Thanks in advance for any suggestions.
-(NSFetchedResultsController *)fetchedResultsController {
//code to carry out fetch
//Sort order specified according to parameters
_theItems =[_fetchedResultsController fetchedObjects];
//at this point fetch should be complete
NSManagedObjectContext *mainMOC = _managedObjectContext;
NSManagedObjectContext*privateContext =[[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[privateContext setParentContext:mainMOC];
[privateContext performBlock:^{
for (int i = 0; i < _theItems.count; i++)
{
Items* oneitem = theItems[i];
oneitem.sortorder = i;
}
NSError *error = nil;
if (![privateContext save:&error]) {
NSLog(#"Error saving context: %#\n%#", [error localizedDescription], [error userInfo]);
abort();
}
[mainMOC performBlockAndWait:^{
NSError *error = nil;
if (![mainMOC save:&error]) {
NSLog(#"Error saving context: %#\n%#", [error localizedDescription], [error userInfo]);
//abort();
}
else {
_managedObjectContext = mainMOC;
}
}];
}];
mainMOC=nil;
privateContext = nil;
return _fectchedResultsController;
}
Edit:
From Apple docs:
NSArray *jsonArray = …; //JSON data to be imported into Core Data
NSManagedObjectContext *moc = …; //Our primary context on the main queue
NSManagedObjectContext *private = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[private setParentContext:moc];
[private performBlock:^{
for (NSDictionary *jsonObject in jsonArray) {
NSManagedObject *mo = …; //Managed object that matches the incoming JSON structure
//update MO with data from the dictionary
}
NSError *error = nil;
if (![private save:&error]) {
NSLog(#"Error saving context: %#\n%#", [error localizedDescription], [error userInfo]);
abort();
}
[moc performBlockAndWait:^{
NSError *error = nil;
if (![moc save:&error]) {
NSLog(#"Error saving context: %#\n%#", [error localizedDescription], [error userInfo]);
abort();
}
}];
}];
Edit 2:
Placing the code in viewdidappear stopped the exception. In addition, I changed the code to following and it "seems" to work. Is this now thread safe?
for (int i = 0; i < _theItems.count; i++)
{
Items* oneitem = theItems[i];
NSManagedObjectID* oneID = oneitem.objectID;
Items *myItem = [privateContext objectWithID:oneID];
myItem.sortorder = i;
}
You cannot access NSManagedObjects from multiple contexts. Assuming [NSFetchedResultsController fetchedObjects] returns objects owned by _managedObjectContext, you cannot then access them from privateContext.
One option might be to pass the NSManagedObjectIDs to the private MOC and use the ID to query the main MOC for the corresponding object.
Does that make sense?

How to update coredata value in Objective-C

I am new in iOS and I am facing a problem regarding to update value of coredata.
For Save
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *device;
if (self.device) {
// Update existing device
[device setValue:GlobalIndexPath forKey:#"key"];
} else {
// Create a new device
NSManagedObject *newDevice = [NSEntityDescription insertNewObjectForEntityForName:#"Device" inManagedObjectContext:context];
[newDevice setValue:GlobalIndexPath forKey:#"key"];
}
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
My code to fetch core data is
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"EntityName" inManagedObjectContext:context]];
NSError *error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
And to update I and using code
NSManagedObject* favoritsGrabbed = [results objectAtIndex:0];
[favoritsGrabbed setValue:#"1" forKey:#"Key"];
Update code not update it add one object.
Note - GlobalIndexPath is a name of string.
But this is not working for me any suggestion. Thanks in Advcance!
You need to save the context every time you make changes to any NSManagedObject and want it to persist. Try this:
NSManagedObject* favoritsGrabbed = [results objectAtIndex:0];
[favoritsGrabbed setValue:#"1" forKey:#"Key"];
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}

fetch request returning null after code removed that inserts data

In the code below (in ViewDidLoad), I import a JSON file into an iOS project, and then persist the data using Core Data and, at the end, perform a fetch request successfully. However, after I remove the code that imports the file and persists the data, the fetch request starts returning (null). Based on the code below, can you explain why this is happening?
id delegate = [[UIApplication sharedApplication] delegate];
self.managedObjectContext = [delegate managedObjectContext];
NSError* err = nil;
NSString* dataPath = [[NSBundle mainBundle] pathForResource:#"inventorydata" ofType:#"json"];
NSArray* inventoryData = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:dataPath]
options:kNilOptions
error:&err];
if(err) NSLog(#"Error %#",[err description]);
NSLog(#"Imported Data: %#", inventoryData);
[Questions enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
Inventory *partsInfo= [NSEntityDescription
insertNewObjectForEntityForName:#"Inventory"
inManagedObjectContext:self.managedObjectContext];
partsInfo.name = [obj objectForKey:#"name"];
partsInfo.sku = [obj objectForKey:#"sku"];
Supplier *supplierInfo = [NSEntityDescription
insertNewObjectForEntityForName:#"Supplier"
inManagedObjectContext:self.managedObjectContext];
supplierInfo.supplierId = [obj objectForKey:#"supplierId"];
[supplierInfo setValue:[NSSet setWithObject:partsInfo ] forKey:#"partData"];
}];
NSError *error;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Supplier"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
self.fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (Supplier *info in self.fetchedObjects) {
NSLog(#"supplierId: %#", info.supplierId);
}
Try this.
[Questions enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
Inventory *partsInfo= [NSEntityDescription
insertNewObjectForEntityForName:#"Inventory"
inManagedObjectContext:self.managedObjectContext];
partsInfo.name = [obj objectForKey:#"name"];
partsInfo.sku = [obj objectForKey:#"sku"];
Supplier *supplierInfo = [NSEntityDescription
insertNewObjectForEntityForName:#"Supplier"
inManagedObjectContext:self.managedObjectContext];
supplierInfo.supplierId = [obj objectForKey:#"supplierId"];
[supplierInfo setValue:[NSSet setWithObject:partsInfo ] forKey:#"partData"];
}];
//Add this code : need to save database.
[delegate saveContext];
// You should add this method in AppDelegate.m
- (void)saveContext{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}

controllerDidChangeContent is blocking main thread

When I try to save data for private NSManagedObjectContext I get deadlock even if operations for this context are called inside performBlock: block. This is strange because this block should be performed asynchronosuly withouth interrupting the main UI/UX thread.
Here is the code of this method:
- (void)loadTimetableToCoreData:(id)timetable
{
// Initializing temporary context
NSManagedObjectContext *tempContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
tempContext.parentContext = self.moc;
[tempContext performBlock:^{
// Here is the parsing
NSLog(#"Finished loading to temp MOC");
NSFetchRequest* r = [NSFetchRequest fetchRequestWithEntityName:#"Timetable"];
[r setIncludesPendingChanges:NO];
NSArray *existingTimetables = [tempContext executeFetchRequest:r error:nil];
for (Timetable *table in existingTimetables) {
[tempContext deleteObject:table];
}
// Saving procedure with multithreading
NSError *error;
if (![tempContext save:&error]) {
NSLog(#"Couldn't save: %#", [error localizedDescription]);
}
NSLog(#"Finished saving to temp MOC");
[self.moc performBlock:^{
// Save groups to presistant store
NSError *error;
if (![self.moc save:&error]) {
NSLog(#"Couldn't save: %#", [error localizedDescription]);
}
NSLog(#"Finished saving to main MOC");
[self.writer performBlock:^{
// Save groups to presistant store
NSError *error;
if (![self.writer save:&error]) {
NSLog(#"Couldn't save: %#", [error localizedDescription]);
}
NSLog(#"Finished saving to writer MOC");
}];
}];
}];
}
As you can see there are some NSLogs inside this method and to visualize my problem I get stuck between Finished loading to temp MOC and Finished saving to temp MOC.
What am I doing wrong and how to unlock the main thread?
Update
Here is the screen from Instruments and what I can see... The controllerDidChangeCintent is killing everything.
I know that this is caused by those lines from loadTimetableToCoreData:
NSFetchRequest* r = [NSFetchRequest fetchRequestWithEntityName:#"Timetable"];
[r setIncludesPendingChanges:NO];
NSArray *existingTimetables = [tempContext executeFetchRequest:r error:nil];
for (Timetable *table in existingTimetables) {
[tempContext deleteObject:table];
}
But I can't get rid of them! Or I don't know how....

How to reset CoreData Application

I am trying, to reset my app which uses Core Data, for this, I have written the code below
- (void) resetApplicationModel
{
// NSError *error;
NSError *error;
// retrieve the store URL
NSURL * storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"abc.sqlite"];
// lock the current context
[self.managedObjectContext lock];
[self.managedObjectContext reset];//to drop pending changes
//delete the store from the current managedObjectContext
if ([[self.managedObjectContext persistentStoreCoordinator] removePersistentStore:[[[self.managedObjectContext persistentStoreCoordinator] persistentStores] lastObject] error:&error])
{
// remove the file containing the data
if([[NSFileManager defaultManager] removeItemAtURL:storeURL error:&error])
{
//recreate the store like in the appDelegate method
if([[self.managedObjectContext persistentStoreCoordinator] addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error] == nil)
{
NSLog( #"could not create new persistent store at %# - %#", [storeURL absoluteString], [error localizedDescription]);
}
}
else
{
NSLog( #"could not remove the store URL at %# - %#", [storeURL absoluteString], [error localizedDescription]);
}
}
else
{
NSLog( #"could not remove persistent store - %#", [error localizedDescription]);
}
[self.managedObjectContext unlock];
__managedObjectContext = nil;
[self managedObjectContext];
_rootViewController.controller = nil;//Make NSFetchResultsController nil;
}
This code works fine on ios 6.1 simulator and there are no issues, but when I check this on iphone 4 running iOS 6.1.3, the persistence file is not deleted and I get error saying
"could not remove the store URL at file://localhost/var/mobile/Applications/147E198E-FAB9-46EA-BE4B-4411AFA013FB/Documents/abc.sqlite - The operation couldn’t be completed. (Cocoa error 4.)"
But all the data is deleted, Now when I try to add new Data inside coreData , I get a error saying
" Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'This NSPersistentStoreCoordinator has no persistent stores. It cannot perform a save operation.'"
Please help me guys
Regards
Ranjit
Create a category on NSManagedObjectContext then implement:
- (BOOL)deleteAllObjectsForEntityName:(NSString *)entity withError:(NSError *__autoreleasing *)error {
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:entity inManagedObjectContext:self]];
[request setIncludesPropertyValues:NO];
NSArray *allObjects = [self executeFetchRequest:request error:error];
if (![self save:error])
return NO;
for (NSManagedObject *object in allObjects)
[self deleteObject:object];
return YES;
}
Then to use this simply do:
- (void)deleteAllObjectsForEntityName:(NSString *) entity {
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
if (![managedObjectContext deleteAllObjectsForEntityName:entity withError:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
}
}
Pass your entity name in and it will delete it all :-)

Resources