Magical Record, Accessing NSManagedObjectContext in NSManagedObject - ios

I'm using Magical Record to manage Core Data in my app which uses Kinvey. On my NSManagedObject I've implemented the following method as a requirement for Kinvey's SDK to work with Core Data.
+ (id)kinveyDesignatedInitializer:(NSDictionary *)jsonDocument
{
NSString* existingID = jsonDocument[KCSEntityKeyId];
Task* obj = nil;
NSManagedObjectContext* context = [NSManagedObjectContext MR_defaultContext];
NSEntityDescription* entity = [NSEntityDescription entityForName:#"Task" inManagedObjectContext:context];
if (existingID) {
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"kinvey_id = %#", existingID];
[request setPredicate:predicate];
NSArray* results = [context executeFetchRequest:request error:NULL];
if (results != nil && results.count > 0) {
obj = results[0];
}
}
if (obj == nil) {
//fall back to creating a new if one if there is an error, or if it is new
obj = [[self alloc] initWithEntity:entity insertIntoManagedObjectContext:context];
}
return obj;
}
However I'm not sure if this is the correct way of obtaining the context for the NSManagedObject.
NSManagedObjectContext* context = [NSManagedObjectContext MR_defaultContext];
Also I'm extremely confused on handling my NSManagedObjectContexts and NSManagedObjects
In one of my current apps I've set my structure as this:
Main App View Controller
- self.editingContext = [NSManagedObjectContext MR_defaultContext];
Detail Object Views
- self.editingContext = [NSManagedObjectContext MR_contextForCurrentThread];
- self.currentObject = [self.editingContext objectWithID:objectID]; (objectID passed from MAVC)
I'm getting reports of the app crashing, so something's obviously not right.
For the new app I was thinking to have the following structure but I'm not sure if it's okay to pass managedobjects and contexts across view controllers.
Main App View Controller
- self.editingContext = [NSManagedObjectContext MR_defaultContext];
- detailVC.currentObject = managedObject;
Detail Object Views
- self.editingContext = self.currentObject.managedObjectContext;
Any help would be really appreciated!

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 Lazy Loading with NSPrivateQueueConcurrencyType and custom setter not working

Problem: Fetching a managed object using a background thread does not lazy load the NSManaged object relationship correctly when the NSManaged object that is related has a custom setter. Doing fetch on main thread with main concurrency type works without a problem. Why is this?
Work Around: If I create a custom getter on the relationship object and check for nil, I can force the NSManaged object to load by calling other variables that don't have custom setter methods.
Background
The core data layout is pretty simple. I have a Game managed object and a Turn managed object. The turn object is a one to one relationship with the game object. I always fetch the game object in order to access the turn object. TurnImp and GameImp are implementation classes that inherit from the Game and Turn object so I don't put getter/setter methods in auto generated code.
Code
The Fetch
//
//Stick command on background
//
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {
//
//Load Game
//
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
CoreDataHelper *coreDataHelper = appDelegate.coreDataHelper;
NSManagedObjectContext *childMOC = [coreDataHelper createChildManagedObjectContext];
//the request
NSFetchRequest *fetchRequest = [NSFetchRequest new];
//the object entity we want
NSEntityDescription *entity = [NSEntityDescription entityForName:GAMEIMP_GAME inManagedObjectContext:childMOC];
[fetchRequest setEntity:entity];
//the predicate rules, the what
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"gameId == %#", #"1404110671234567"];
[fetchRequest setPredicate:predicate];
//the sorting rules
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]initWithKey:GAMEIMP_OBJECT_ID ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
//Fetch results
NSFetchedResultsController *resultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:childMOC sectionNameKeyPath:nil cacheName:nil];
NSError *error;
BOOL success = [resultsController performFetch:&error];
GameImp *game;
if (success) {
game = [resultsController.fetchedObjects objectAtIndex:0];
} else {
NSLog(#"Unable to get game. Error: %#", error);
}
TurnImp *turnImp = game.turn;
//Issue is here!!! Should be 3, instead 0 because lastRoundReward is nil.
int lastRoundReward = [turnImp.lastRoundReward intValue];
//Work around, call custom getter method. Now 3 is returned.
lastRoundReward = [turnImp getLastRoundReward];
}
This childMOC creation
-(NSManagedObjectContext*) createChildManagedObjectContext {
NSManagedObjectContext *childMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
childMOC.parentContext = self.mainManagedObjectContext;
return childMOC;
}
TurnImp Header
#interface TurnImp : Turn
#property(atomic) BOOL isValid;
- (void) setLastRoundReward: (int) lastRoundReward;
- (int) getLastRoundReward;
#end
TurnImp M
#implementation TurnImp
#synthesize isValid;
#synthesize lastRoundReward = _lastRoundReward;
/**
* Set the last round reward
* #param -
* #return -
*/
- (void) setLastRoundReward: (int) lastRoundReward {
_lastRoundReward = [NSNumber numberWithInt:lastRoundReward];
}
/**
* Get the int value of lastRoundReward
*/
- (int) getLastRoundReward {
//Note - HACK! Lazy loading not working, try another member
if (self.lastRoundReward == nil) {
//Force load
NSString *objectId = self.objectId;
}
return [self.lastRoundReward intValue];
}
Change childMoc to mainMoc and it works. MainMoc Code
//create the main MOC
_mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
More After Fixed Concurrency issue
[childMOC performBlock:^{
// Execute the fetch on the childMOC and do your other work.
NSError *error;
NSArray *results = [childMOC executeFetchRequest:fetchRequest error:&error];
if (results == nil) {
// Handle error
} else if (results.count == 1) {
GameImp *game = [results firstObject];
TurnImp *turnImp = game.turn;
//Issue is here!!! Should be 3, instead 0 because lastRoundReward is nil.
int lastRoundReward = [turnImp.lastRoundReward intValue];
//Work around, call variable objectId (not same as ObjectId)
NSString *objectId = turnImp.objectId;
//not it's 3...
lastRoundReward = [turnImp.lastRoundReward intValue];
}
}];
Work Around
I removed the following from TurnImp and it works as expected with the relationships.
#synthesize lastRoundReward = _lastRoundReward;
First, I have to confess that I have no idea what your problem statement means - what is lazy loading of a relationship supposed to do anyway?
However, a quick glance at your code reveals that you are creating a MOC with NSPrivateQueueConcurrencyType yet you are not properly wrapping its use inside an appropriate performBlock invocation.
When you clearly violate the Core Data Concurrency guidelines, you are playing in dangerous waters and will get undefined behavior.
Also, why create an instance of NSFetchedResultsController just to perform a fetch? That's overkill. Simply use a fetch request. Like so...
[childMOC performBlock:^{
// Execute the fetch on the childMOC and do your other work.
NSError *error;
NSArray *results = [childMOC executeFetchRequest:fetchRequest error:&error];
if (result == nil) {
// Handle error
} else if (results.count == 1) {
GameImp *game = [results firstObject];
TurnImp *turnImp = game.turn;
int lastRoundReward = [turn2.lastRoundReward intValue];
}
}];

How to update transformable attribute in core data

I am in a situation where i need to update transformable attribute in my entity in core data, until now i've tried every possible answer from google and stack overflow but did't achieve anything.
This is the method where i am saving object in core data, and my object which i am saving is an NSMutablDictionary type object.
-(void)didSaveToCoreData :(NSMutableDictionary *)newDict
{
#try {
AppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
NSManagedObjectContext *context = appDelegate.managedObjectContext ;
DataModelSupport *entity = [NSEntityDescription insertNewObjectForEntityForName:#"CPIEntity" inManagedObjectContext:context];
if (newDict != nil) {
[entity.fixed_Model removeAllObjects];
entity.fixed_Model = newDict;
}
NSError *error ;
[context save:&error];
if(error)
{
NSLog(#"Error in Saving Data");
}
else
{
[self didFetchFromCoreDataModel];
NSLog(#"Successfully saved");
}
}
#catch (NSException *exception) {
[self spareMeFromTheCrash:exception];
}
#finally {
}
}
in this method i am saving a dictionary object of 19 key/value, at the first time and i am fetching it correctly in didFetchFromCoreDataModel method, but when i refresh the data and get dictionary of 18 key/value i save that dictionary in core data using the same method didSaveToCoreData and fetch it in the same way from didFetchFromCoreDataModel but it still show 19 key/value
DataModelSupport is a subclass of NSManagedObject.
In DataModelSupport.h:
#property (nonatomic,weak) NSMutableDictionary *fixed_Model;
In DataModelSupport.m:
#dynamic fixed_Model;
This is it for the DataModelSupport class.
Now here in this method i am fetching the same object form core data
-(void)didFetchFromCoreDataModel
{
#try {
AppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
NSManagedObjectContext *context = appDelegate.managedObjectContext ;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"CPIEntity" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
[request setReturnsDistinctResults:YES];
[request setReturnsObjectsAsFaults:NO];
[request setResultType:NSDictionaryResultType];
[request setEntity:entity];
NSError *error ;
NSArray *arr = [context executeFetchRequest:request error:&error];
updatedfinalArr = [arr valueForKey:#"fixed_Model"];
if(error)
{
NSLog(#"Error");
}
else
{
}
}
#catch (NSException *exception) {
[self spareMeFromTheCrash:exception];
}
#finally {
}
}
And this is how my core data looks like:-
Any help is appreciated.
EDIT
I've implemented some changes in my code now in didSaveToCoreData method i am using this line of code to fetch the Entity by name
NSEntityDescription *descriptor = [NSEntityDescription entityForName:#"CPIEntity" inManagedObjectContext:context];
by this i am not creating new entity every time i call didSaveToCoreData method.
and this is how i am saving NSMutlableDictionary object
DataModelSupport *entity = [[DataModelSupport alloc]initWithEntity:descriptor insertIntoManagedObjectContext:context];
[entity.fixed_Model removeAllObjects]
entity.fixed_Model = newDict;
but still i am not getting correct result.
now when i refresh the data and save it using the above procedure explained in EDIT section, and fetch it, i get the updated data but it increase the number of objects, like on first attempt when i fetch i got 1 object in array, and on second attempt i got 2 objects and it goes like this, so when ever new data is added its not updating it but instead it add it in the entity s fixed_Model attribute and increase the number of object.
Lastly now i am using this line of code to get the last and update object from array in didFetchFromCoreDataModel method
NSDictionary *ddd = [[arr valueForKey:#"fixed_Model"]lastObject];
updatedfinalArr = [NSMutableArray arrayWithObject:ddd];
Your save method creates a new CPIEntity object each time. So, unless you delete the old object elsewhere in your code, I suspect your fetch is returning several objects, the first of which has the dictionary with 19 key/value pairs in the fixed_Model attribute, and the second/subsequent objects contain the 18 key/value pairs.
When you save, you should try to fetch the existing object first, and if you get zero results then create a new object. Then set the fixed_Model attribute of the new/existing object to your new dictionary.
EDIT
You are still inserting a new object each time (DataModelSupport *entity = [[DataModelSupport alloc]initWithEntity:descriptor insertIntoManagedObjectContext:context];). See below for an example of "fetch or create":
AppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
NSManagedObjectContext *context = appDelegate.managedObjectContext ;
NSEntityDescription *descriptor = [NSEntityDescription entityForName:#"CPIEntity" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
request.entity = descriptor;
NSError *error;
NSArray *results = [context executeFetchRequest:request error:&error];
if (results == nil) {
// This implies an error has occurred.
NSLog(#"Error from Core Data: %#", error);
} else {
if (results.count == 0) {
// No objects saved, create a new one...
DataModelSupport *entity = [[DataModelSupport alloc]initWithEntity:descriptor insertIntoManagedObjectContext:context];
entity.fixed_Model = newDict;
} else {
// At least one object saved. There should be only one
// so use the first...
DataModelSupport *entity = [results firstObject];
entity.fixed_Model = newDict;
}
}
I've assumed for simplicity that newDict is not nil; amend as appropriate to handle that case.
Can you narrow down the problem?
Ie. can you compare the two Dictionaries..the original one with 19 values and the new one with 18 values?
Is there a particular entry which is not being 'removed'? That might point to a challenge with 'delete' (or the lack there of).
Alternatively, if you completely replace the content, what result do you get on fetch?

the object is not being turned into a fault

The problem randomly occurs...
Crashing Location (which is a method in NSOperationQueue)
[self.requestOperationQueue addOperationWithBlock: ^{
NSArray *titleList = [[NSMutableArray alloc] init];
NSArray *allBooks = [[CoreDataManager sharedInstance] fetchBooks];
for (Book *book in allBooks)
[titleList addObject:book.title]; // program crashed here!! failed to fault the value of book.title
}];
I use managedObjectContentChild for NSEntityDescription.
However, executeFetchRequest by managedObjectContent, which is the parent of managedObjectContentChild.
Is that the potential problem?
I tried executeFetchRequest by managedObjectContentChild, however, it leads many more different issues.
However, I am binded to use managedObjectContentChild since program is running in multiple threads by create new CoreDataManager instance for individual thread. Program will run into deadlock without using children MOC.
Thanks in advance!
CoreDataManager.m
- (id)init
{
if ((self = [super init]) != nil)
{
delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
// Writer (write data to Persistent Store Coordinator)
writerManagedObjectContext = [delegate writerManagedObjectContext];
// Parent (Fetched Result Controller)
managedObjectContext = [delegate managedObjectContext];
// Child (handling Object Context Saving for individual threads)
managedObjectContextChild = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
managedObjectContextChild.parentContext = managedObjectContext;
bookEntity = [NSEntityDescription entityForName:[Book description]
inManagedObjectContext:managedObjectContextChild];
friendEntity = [NSEntityDescription entityForName:[Friend description]
inManagedObjectContext:managedObjectContextChild];
}
return self;
}
- (NSArray *)fetchBooks
{
// Todo: fix the problem of "CoreData: error: NULL _cd_rawData but the object is not being turned into a fault"
NSArray *results = nil;
if (key == nil)
return results;
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:key ascending:ascending];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
[request setPredicate:predicate];
[request setSortDescriptors:sortDescriptors];
NSError *error = NULL;
// results = [managedObjectContextChild executeFetchRequest:request error:&error];
results = [managedObjectContext executeFetchRequest:request error:&error];
if (error != NULL)
NSLog(#"Error fetching - %#", error);
return results;
}
I tried to figure out the following discussion, but still have no clue how to do it...
CoreData: error: NULL _cd_rawData but the object is not being turned into a fault
http://www.cocoabuilder.com/archive/cocoa/311615-weird-core-data-crash.html
Here is the problem,
// use this one
results = [managedObjectContextChild executeFetchRequest:request error:&error];
// not this
// results = [managedObjectContext executeFetchRequest:request error:&error];
so I use managedObjectContextChild (child MOC) instead of managedObjectContext (parent MOC) in order to create distinct MOC for each individual threads. As a rules of concurrency of CoreData.
using managedObjectContext (parent MOC) will not cause the error of object not turning to fault and crashes the app every single time, but it's serious issue if the app happened to be using the same MOC (well, there is only one managedObjectContext in this case) at the exactly the same moment even from different threads.

error trying to access core data

I am trying to get the values I have saved in one of my core data objects, however I keep getting this error
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+entityForName: nil is not a legal NSManagedObjectContext parameter searching for entity name 'Man''
The resulted error is occurring from the code I have written that tries to access the core data object then logg its content, as show shown below.
if ([manufDBVNumber isEqualToNumber:currentDBVersion]) {
// READ FROM CORE DATA
// WRITE TO CORE DATA
NSManagedObjectContext *context = [self managedObjectContext];
NSLog(#"%#", context);
// Test listing all FailedBankInfos from the store
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Man" inManagedObjectContext:self.managedObjectContext]; // This is where the error is happening.
[fetchRequest setEntity:entity];
NSError *error;
NSMutableArray *manufacturerDictionaryArray = [[NSMutableArray alloc] init];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (Manuf *manuf in fetchedObjects) {
NSLog(#"MANA: %#", man.mana);
NSLog(#"SURGE: %#", man.surge);
NSLog(#"HEALTH: %#", manuf.health);
etc//....
This is how I have synthesized the managedObjectContext and fetchedResultsController
// Core-Data variables used for fetching and managing the object
#synthesize fetchedResultsController = __fetchedResultsController;
#synthesize managedObjectContext = __managedObjectContext;
Any help would be greatly appreciated.
You do this:
NSManagedObjectContext *context = [self managedObjectContext];
But then you access this:
self.managedObjectContext
Assuming your NSLog(#"%#", context); shows a valid NSManagedObjectContext, then I would do this:
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Man" inManagedObjectContext:context];
Or, change your original declaration to:
self.managedObjectContext = [self managedObjectContext];
In your viewController.m implementation file, right under this bit of code:
- (void)viewDidLoad
{
add this bit of code:
id delegate = [[UIApplication sharedApplication] delegate];
self.managedObjectContext = [delegate managedObjectContext];

Resources