Core data debugger hangs at assigning value to entity - objective-c

I have a core data entity to which I am trying to assign relationship from another entity. Please refer the code below
#define kId #"id"
-(NSArray *)fetchObjectsForEntityName:(NSString *)entityName withPredicate:(NSPredicate *)predicate
{
NSManagedObjectContext *newContext = [Helper generateNewContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:newContext];
NSFetchRequest *request = [NSFetchRequest new];
[request setEntity:entity];
if (predicate)
[request setPredicate:predicate];
NSError *error = nil;
NSArray *resultArray = [newContext executeFetchRequest:request error:&error];
return resultArray;
}
-(void)updateCoreDataEntity
{
NSArray *objectsArray = [self fetchObjectsForEntityName:#"FirstEntity" withPredicate:nil];
//FirstObjects is a subclass of NSManagedObject class (Custom entity)
//kId is just #define as defined above
//Recasting removed
for (FirstObjects *firstObject in objectsArray) {
if ([firstObject.id isEqualToString:[dict valueForKey:kId]]) {
secondEntity.firstEntity = firstObject; //debugger hangs here
}
}
}
I am trying to fetch objects that belong to "FirstEntity" into an NSArray
Loop through that array to find the required object.
Then assign the "firstObject" to SecondEntity if the criteria matches.
However, I am getting nowhere with this code as the debugger (and the code) hangs at the last line of code.
What is the mistake I am doing, can anyone help with this code.
Regards,
iSee

secondEntity is maybe undefined. This would surely lead to a crash.
Also, the logic of the ID is flawed. It seems the comparison is not comparing to a specific ID but to the generic string "id". Perhaps you really want to compare to a dynamically allocated id? Also, are these string ids unique? (If not you might get unpredictable results.)
Finally, from the code it is not clear if Helper provides always the same managed object context. This would be strongly advised - separate contexts are mainly used for concurrency.

Related

Design pattern to deal with similar methods

I have two methods in my objective-c class that do similar but different things. (they both retrieve a set of core-data records containing JSON, unpack and examine the JSON documents and do some stuff depending upon the structure of the JSON).
The first method looks like this:
+(NSDictionary*)getListOfResponsesWithForm:(NSString*)formId
{
NSError* requestError = nil;
// NSIndexSets don't allow easy targetted access into the set, so use arrays instead.
NSMutableArray* indexSetOfAllEntries = [[NSMutableArray alloc] init];
NSMutableArray* indexSetOfEntriesForLoggedOnUser = [[NSMutableArray alloc] init];
NSString* activeUserEmail = getActiveUser().email;
NSFetchRequest* fetchRequest = [ [NSFetchRequest alloc] init];
NSEntityDescription* entityDesc = [NSEntityDescription entityForName:#"Response" inManagedObjectContext:getApp().managedObjectContext];
[fetchRequest setEntity:entityDesc];
// Sort by lastmodifieddatelocal
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"lastmodifieddatelocal" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
NSArray* instances = [getApp().managedObjectContext executeFetchRequest:fetchRequest error:&requestError];
NSMutableArray* responses = [[NSMutableArray alloc] init];
for (Response* response in instances) {
NSData *jsonData = [response.json dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary* dictionary = [HPSJSON getDictionaryFromDataWithoutSuccessTest:jsonData];
NSString* userEmailFromResponse = [HPSJSON getStringForKey: #"_useremail" inDictionary:dictionary];
NSString* formIdFromResponse = [HPSJSON getNestedIdForKey: #"_formid" inDictionary: dictionary];
if ([formId caseInsensitiveCompare:formIdFromResponse]==NSOrderedSame)
{
[responses addObject: response];
[indexSetOfAllEntries addObject:[NSNumber numberWithInt:responses.count-1]];
if ([activeUserEmail caseInsensitiveCompare:userEmailFromResponse]==NSOrderedSame)
{
[indexSetOfEntriesForLoggedOnUser addObject:[NSNumber numberWithInt:responses.count-1]];
}
}
}
NSMutableDictionary* results = [[NSMutableDictionary alloc] init];
[results setObject:responses forKey:#"responses"];
[results setObject:indexSetOfAllEntries forKey:#"allindexes"];
[results setObject:indexSetOfEntriesForLoggedOnUser forKey:#"indexesforactiveuser"];
return results;
}
The second method looks like this:
+(NSInteger)getCountOfResponsesWithForm:(NSString*)formId
{
NSError* requestError = nil;
NSString* activeUserEmail = getActiveUser().email;
NSFetchRequest* fetchRequest = [ [NSFetchRequest alloc] init];
NSEntityDescription* entityDesc = [NSEntityDescription entityForName:#"Response" inManagedObjectContext:getApp().managedObjectContext];
[fetchRequest setEntity:entityDesc];
NSArray* instances = [getApp().managedObjectContext executeFetchRequest:fetchRequest error:&requestError];
NSInteger countOfResponses=0;
for (Response* response in instances) {
NSData *jsonData = [response.json dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary* dictionary = [HPSJSON getDictionaryFromDataWithoutSuccessTest:jsonData];
NSString* userEmailFromResponse = [HPSJSON getStringForKey: #"_useremail" inDictionary:dictionary];
NSString* formIdFromResponse = [HPSJSON getNestedIdForKey: #"_formid" inDictionary: dictionary];
if ([formId caseInsensitiveCompare:formIdFromResponse]==NSOrderedSame)
{
if ([activeUserEmail caseInsensitiveCompare:userEmailFromResponse]==NSOrderedSame)
{
countOfResponses++;
}
}
}
return countOfResponses;
}
There is quite a lot of duplicate code here, and I feel I am abusing DRY to some extent. However, attempting to combine the methods into one method will introduce some complication that will somewhat obfuscate what each individual method does.
Can anyone offer advice on the most elegant way to implement the functionality that is encompassed on both methods? Stay with the two methods? Create one more complex method sprinkled with conditions? Break out into a different class?
Is their a relevant design pattern for this scenario? Thanks.
I would suggest:
identify common blocks (e.g., NSFetchRequest* fetchRequest..., NSEntityDescription* entityDesc);
for each common block, define a method that gets the proper arguments and does what is required of them;
call such method to improve on DRY, e.g.:
<getInstances>
<for each instance>
<extract data from instance>
<do your processing on data>
I would not think in terms of design patterns, rather in terms of refactoring in this case.
You could of course encapsulate all this in a class of its own, but I do not know the role played by the class presently containing your code, so I cannot say anything more detailed.
BTW, I tend to use design patterns at a higher level of abstraction (that is why they are called design pattern, although many are mostly useful at the architectural level). In this case some refactoring recipes (as you might find googling for them) are better suited I think.
I would consider Template design pattern.
It basically encapsultes basic behavior in template method inside the base class and conrete behavior is implemented inside underlying child classes that implements (or overrides) abstract methods defined in base class.
Interesting resource also here on SO: Objective-C - Template methods pattern?
Use design pattern Visitor or Command. This lets you decouple fetching and iterating over the responses from what action to take.
What you have here are two things: A preparation + loop + marshalling step and a processing step. One part is the same (the former) and one varies (the latter). So you want to factor out the latter, so you can avoid making several copies of the former.
You can do so either by making the processing step a dummy method that is overloaded in subclasses, or by passing in "something" that can perform the step. This "something" will usually be either a Command or a Visitor.
It all really boils down to whether you want to use inheritance or composition. Personally I tend to prefer composition.
I am not good with Xcode, so let me just write some logic instead.
It seems to me that your second method can be simplified by :
int getCountOfResponsesWithForm(String a)
{
Dictionary dic = getListOfResponsesWithForm(a);
return dic.length();
}
You might interested to read Facade Design Pattern.

Does NSStringFromClass([MyEntityClass class]) generate a safe Core Data Entity name?

Most (all I've seen) Core Data tutorials use the following code snippet with #"MyEntityClass" hard-coded in:
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"MyEntityClass"];
Is it safe to use NSStringFromClass() as an Entity Name?
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClass([MyEntityClass class])];
This seams to be much easer to deal with regarding refactoring and the like. Especially since I am having Xcode create my NSManagedObject subclasses. I ask because I have never seen this before, so perhaps I am missing something.
Yes, that code is fine, if your entity's class is set to MyEntityClass in your model.
I prefer to give the entity class a class method that returns the entity name:
+ (NSString *)entityName {
return NSStringFromClass(self);
}
and call it like this:
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:[MyEntityClass entityName]];
This way, if I want to change the class name without changing the entity name in the model, I can just make the change in the class method:
+ (NSString *)entityName {
return #"NewEntityName";
}
Why would I do that? Well, I might decide on a better name for the entity. Changing the class name doesn't break compatibility with an existing Core Data persistent store, but changing the entity name in the model file does. I can change the class name, and the entityName method, but leave the entity name unchanged in the model, and then I don't have to worry about migration. (Lightweight migration supports renamed entities so it's not that big of a deal either way.)
You could go further and actually have the entityName method look up the entity name from the managed object model at runtime. Suppose your application delegate has a message that returns the managed object model:
+ (NSString *)entityName {
static NSString *name;
static dispatch_once_t once;
dispatch_once(&once, ^{
NSString *myName = NSStringFromClass(self);
NSManagedObjectModel *model = [(AppDelegate *)[UIApplication delegate] managedObjectModel];
for (NSEntityDescription *description in model.entities) {
if ([description.managedObjectClassName isEqualToString:myName]) {
name = description.name;
break;
}
}
[NSException raise:NSInvalidArgumentException
format:#"no entity found that uses %# as its class", myName];
});
return name;
}
Obviously, if you really want to do this, you should factor out the contents of the dispatch_once block into a helper method, probably on your app delegate (or wherever you get the model).

What's the Relationship Between Fetch and One Way Relationship in Core Data?

Why "Fetch" means one way relationship
What is the relation between fetch and one way?
Well, I'm not sure I understand your question, but I'll try and answer in the context of a simple domain model.
Let's say you have a Person class and a Computer class, both extend NSManagedObject. Person is linked to Computer via a one-way relationship, a NSSet named computers.
This would allow you to say in your code:
Person *person = // ... (loaded from database)
NSSet *computers = person.computers;
for (Computer *computer : computers) {
// do something fun
}
However, since it's a one-way relationship, you would not be able to do this:
Computer *computer = // loaded from DB
Person *person = computer.person; // This line won't work
Therefore, since it won't work -- there's no backwards relationship -- the only way to get the related Person class would be to build a Fetch Request to query the database and find the Person instances that have the Computer as a member.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSPredicate *predicate = // define your NSPredicate to find the Person object here
[fetchRequest setPredicate:predicate];
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
Generally speaking, that's annoying. So, it's generally a good idea to define your relationships bi-directionally (e.g. with inverse) if possible, that would enable you to just do:
Person *person = computer.person;

Can I put NSFetchedResultsController inside a Managed Object Class?

I'm sick and tired of constantly having to putting/repeat the NSFetchedResultsController code for my project in virtually every file where I'm working with the Managed Object Context.
I want to reduce the amount of repetitive code. I want to put this kind of repetitive CRUD-like code inside the model class.
What I want instead is put all my custom NSFetchs inside the Managed Object Class for the relevant Entity (ie: Company.m, Employee.m).
Even the Core Books sample code from Apple does not put this code into the Managed Object Class and I'm wondering if its possible?
I tried pasting the code into my Company.m class but it keeps complaining about the managedObjectContext and also it complains that fetchedResultsController is not declared, even though its a parameter?
Ideally, I would like to put lots of different kinds of fetch request/results controller stuff inside the Entity Managed Object Class too.
But before I get ahead of myself, Is it possible, therefore, just to put all the NSFetchedResultsController stuff inside the Entity Managed Object class?
If there is a sample tutorial or project or source code that covers this, that'd be great too.
Thanks.
(code sample follows).
/**
Returns the fetched results controller. Creates and configures the controller if necessary.
*/
- (NSFetchedResultsController *)fetchedResultsController
{
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
// Create and configure a fetch request with the Book entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Company" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
// Create the sort descriptors array.
NSSortDescriptor *authorDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:authorDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Create and initialize the fetch results controller.
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:#"author" cacheName:#"Root"];
self.fetchedResultsController = aFetchedResultsController;
fetchedResultsController.delegate = self;
// Memory management.
[aFetchedResultsController release];
[fetchRequest release];
[authorDescriptor release];
[sortDescriptors release];
return fetchedResultsController;
}
I recommend using ActiveRecord with xmod. CoreData will overwrite your CRUD if you modify your core data model. ActiveRecord makes it as easy as calling [MyManagedObject createEntity]; and NSArray *myObjects = [MyManagedObject findAll]; There is also options to pass predicates to filter the findAll call. The xmod addition generates a subclass to the generated classes so that you can add custom logic to your entities so that they do not get overridden.
Edit: I would like to add the link to this Active Record implementation since this is the one I actually use.
Edit2: This has now been renamed to Magical Record.

NSTokenField representing Core Data to-many relationship

I'm having a problem figuring out how to represent a many-to-many relationship model in a NSTokenField. I have two (relevant) models:
Item
Tag
An item can have many tags and a tag can have many items. So it's an inverse to-many relationship.
What I would like to do is represent these tags in a NSTokenField. I would like to end up with a tokenfield automatically suggesting matches (found out a way to do that with tokenfield:completionsForSubstring:indexOfToken:indexOfSelectedItem) and being able to add new tag entities if it wasn't matched to an existing one.
Okay, hope you're still with me. I'm trying to do all this with bindings and array controllers (since that makes most sense, right?)
I have an array controller, "Item Array Controller", that is bound to my app delegates managedObjectContext. A tableview showing all items has a binding to this array controller.
My NSTokenField's value has a binding to the array controllers selection key and the model key path: tags.
With this config, the NSTokenField won't show the tags. It just gives me:
<NSTokenFieldCell: 0x10014dc60>: Unknown object type assigned (Relationship objects for {(
<NSManagedObject: 0x10059bdc0> (entity: Tag; id: 0x10016d6e0 <x-coredata://9D77D47A-1171-4397-9777-706F599D7E3B/Tag/p102> ; data: <fault>)
)} on 0x100169660). Ignoring...
This makes sense to me, so no worries. I've looked at some of the NSTokenField delegate methods and it seems that I should use:
- (NSString *)tokenField:(NSTokenField *)tokenField displayStringForRepresentedObject:(id)representedObject
Problem is, this method is not called and I get the same error as before.
Alright, so my next move was to try and make a ValueTransformer. Transforming from an array with tag entity -> array with strings (tag names) was all good. The other way is more challenging.
What I've tried is to look up every name in my shared app delegate managed object context and return the matching tags. This gives me a problem with different managed object contexts apparently:
Illegal attempt to establish a relationship 'tags' between objects in different contexts (source = <NSManagedObject: 0x100156900> (entity: Item; id: 0x1003b22b0 <x-coredata://9D77D47A-1171-4397-9777-706F599D7E3B/Item/p106> ; data: {
author = "0x1003b1b30 <x-coredata://9D77D47A-1171-4397-9777-706F599D7E3B/Author/p103>";
createdAt = nil;
filePath = nil;
tags = (
);
title = "Great presentation";
type = "0x1003b1150 <x-coredata://9D77D47A-1171-4397-9777-706F599D7E3B/Type/p104>";
}) , destination = <NSManagedObject: 0x114d08100> (entity: Tag; id: 0x100146b40 <x-coredata://9D77D47A-1171-4397-9777-706F599D7E3B/Tag/p102> ; data: <fault>))
Where am I going wrong? How do I resolve this? Is it even the right approach (seems weird to me that you woud have to use a ValueTransformer?)
Thanks in advance!
I've written a custom NSValueTransformer to map between the bound NSManagedObject/Tag NSSet and the NSString NSArray of the token field. Here are the 2 methods:
- (id)transformedValue:(id)value {
if ([value isKindOfClass:[NSSet class]]) {
NSSet *set = (NSSet *)value;
NSMutableArray *ary = [NSMutableArray arrayWithCapacity:[set count]];
for (Tag *tag in [set allObjects]) {
[ary addObject:tag.name];
}
return ary;
}
return nil;
}
- (id)reverseTransformedValue:(id)value {
if ([value isKindOfClass:[NSArray class]]) {
NSArray *ary = (NSArray *)value;
// Check each NSString in the array representing a Tag name if a corresponding
// tag managed object already exists
NSMutableSet *tagSet = [NSMutableSet setWithCapacity:[ary count]];
for (NSString *tagName in ary) {
NSManagedObjectContext *context = [[NSApp delegate] managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSPredicate *searchFilter = [NSPredicate predicateWithFormat:#"name = %#", tagName];
NSEntityDescription *entity = [NSEntityDescription entityForName:[Tag className] inManagedObjectContext:context];
[request setEntity:entity];
[request setPredicate:searchFilter];
NSError *error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
if ([results count] > 0) {
[tagSet addObjectsFromArray:results];
}
else {
Tag *tag = [[Tag alloc] initWithEntity:entity insertIntoManagedObjectContext:context];
tag.name = tagName;
[tagSet addObject:tag];
[tag release];
}
}
return tagSet;
}
return nil;
}
CoreData seems to automatically establish the object relationships on return (but I have not completely verified this yet)
Hope it helps.
Your second error is caused by having two separate managed object context with the same model and store active at the same time. You are trying to create an object in one context and then relate it another object in the second context. That is not allowed. You need to lose the second context and make all your relationships within a single context.
Your initial error is caused by an incomplete keypath. From your description it sounds like you are trying to populate the token fields with ItemsArrayController.selectedItem.tags but that will just return a Tag object which the token filed cannot use. Instead, you need to provide it with something that converts to a string e.g. ItemsArrayController.selectedItem.tags.name
2 questions:
1) Do you have an NSManagedObjectContext being used other than your app delegate's context?
2) Is the object that implements tokenField:displayStringForRepresentedObject: set as the delegate for the NSTokenField?