Design pattern to deal with similar methods - objective-c

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.

Related

Core data debugger hangs at assigning value to entity

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.

What is the more efficient way of sorting and filtering Core Data relationships?

Assuming I have a Podcast entity, which has-many Episodes, I'm confused as to which of these would be the preferred option for culling and sorting:
// Always work with the relationship property
- (NSSet*)unfinishedEpisodes {
NSArray* episodes = self.episodes.allObjects;
NSPredicate* predicate = [NSPredicate predicateWithBlock:^BOOL(PodcastEpisode* episode, NSDictionary* bindings) {
return !episode.isFinished;
}];
NSArray* unfinishedEpisodes = [episodes filteredArrayUsingPredicate:predicate];
return [NSSet setWithArray:unfinishedEpisodes];
}
- (NSArray*)unfinishedEpisodesSortedByAge {
NSSortDescriptor* sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"date" ascending:YES];
return [self.unfinishedEpisodes.allObjects sortedArrayUsingDescriptors:sortDescriptors];
}
or
// Fetch specific sets of data as needed
- (NSArray*)unfinishedEpisodes:(NSArray*)sortDescriptors {
NSFetchRequest* fetch = [[NSFetchRequest alloc] initWithEntityName:#"Episode"];
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"podcast == %# AND playcount == 0", self];
fetch.predicate = predicate;
fetch.sortDescriptors = sortDescriptors;
NSArray* results = [KRTDataManager.sharedManager.mainObjectContext executeFetchRequest:fetch error:nil];
return results;
}
- (NSArray*)unfinishedEpisodesSortedByAge {
NSSortDescriptor* sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"date" ascending:YES];
return [self unfinishedEpisodes:#[ sortDescriptor ]];
}
The part of me that would do as much as possible in SQL has a hard time thinking option 1 is better, but most of what I've read seems to indicate using the (NSSet*)episodes is extremely cheap once the podcast object comes into existence. My understanding of how Core data faults get handled in these situations is pretty shaky, and I realize Core data shouldn't really be compared to a SQL database. Simply based on how the two options are constructed, though, I would think there's some benefit to baking the predicate and sortdescriptors right into the fetch; but maybe it's not enough to make up for the gains that set provided by the relationship give.
Thanks.
First code will be inefficient when working with large result sets as it operates on in memory objects. All objects have to be loaded into memory, so If they aren't already there, they will be loaded one by one as faults are raised.
Second code will do everything on sql side and give You already filtered and sorted results. CoreData also uses internal caching to optimize such queries, so I would prefer this option if episodes aren't already loaded somewhere else. Enable CoreData debug to see how sql queries will look. There is already an answer how to do that.

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.