Avoid repeating code in NSManagedObject categories - ios

In my entity model I have a top-level "Installation" entity, which has a child "cards" relationship. I also have a "Person" entity, which has a child "cards" relationship.
I've written some code which will sort the NSSet of cards to return a specific subset (called sortedCards), and this function can be performed at either the Installation level, or at the Person level.
For exmaple, I want to be able to call:
NSArray *cards = [installation sortedCards];
as well as:
NSArray *cards = [person sortedCards];
Where am I supposed to put this code so that I don't copy the code in two places? I started by putting it in the Installation NSManagedObject category that I created. But if I do that, i need to copy the code into the Person category as well.
Should I put in an NSSet category and call [installation.cards sortedCards] and [person.cards sortedCards]? That doesn't feel right either.
Any help much appreciated.
Duncan

You should place this method in NSSet category, because categories are used for extending basic functionality. And if you need sort NSSet in different places, it should be NSSet category work, not other object or class.

Related

iOS Core data issue

I have made a many-to-many relationship. At first I insert all the data in table 1, the rest of the data isn't available right away.
When the data is available I like to connect it to the right table 1 entries. Should I query table 1 and then set the NSSet with the returned data? Or how would one do this?
To elaborate my question here the example:
[ActivityTable] <<--->> [BannerTable]
At viewDidLoad all the activity are upserted in the ActivityTable. Then the banners from the first activity (first upcoming date) is found from the server.
I got the two (it is always two) banners available but how do I set this?
Used this with help of the answer:
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Banner" inManagedObjectContext:context];
Banner *banner = [[Banner alloc] initWithEntity:entity insertIntoManagedObjectContext:context];
banner.image = shieldDictionary[BANNER_IMAGE];
Assuming you are using the generated NSManagedObject subclasses for your entities...
In your CoreData model each relationship has a name.
Your generated entity classes already contain methods to add/remove relationships you defined in the model. So all you have to do is use one of those methods.
Example:
If the tables are called 'ActivityTable' and 'BannerTable', and the relationship in 'ActivityTable' is called 'banners', then the generated methods look like:
- addBannersObject:(BannerTable *)value;
- removeBannersObject:(BannerTable *)value;
- addBanners:(NSSet *)value;
- removeBanners:(NSSet *)value;
Core Data manages a graph of objects, not tables.
The easiest way to realize many-to-many relationships is to use the dynamically-generated accessors like add<Key>Object: and remove<Key>s:.
For instance:
[anActivity addBannerObject:aBanner];
You can use the data model editor to generate class files for your models which declare these generated methods. You can also use -mutableSetValueForKey: to get a proxy that lets you add and remove objects from a relationship. i.e.:
NSMutableSet *banners = [anActivity mutableSetValueForKey:#"banners"];
[banners addObject:aBanner];
It is obviously less verbose to use the generated methods for each relationship, but it achieves the same thing.

Need assistance regarding core data

I am new to core data and trying to create a simple apps using core data. I am currently working on app to save data in to-many relationship, there are several questions and tutorials but I am still confused.
I have two entities person and contactNumbers, I am fetching person and its contact numbers from address book, person A has mobile number, iphone number, home, work, other... which creates to-many relationship.
In my code I didnt subclass any entity, Is it necessary to subclass entities to save data in relationship? I am asking this because I have read that its not necessary to subclass direct working with NSManagedObject class will do the job.
All I want is to save person A first name, last name in person entity and its contact numbers in contactNumbers entity. How to save data in to-many relationship using core data?
Please I request do not refer other stackoverflow questions, raywenderlich tutorial, app code blog tutorials etc.
Please provide a clearly understandable code with concepts to deal this situation. Thanks a lot.
This is my model
if I Do this
NSManagedObject *person = [NSEntityDescription insertNewObjectForEntityForName:#"person" inManagedObjectContext:self.managedObjectContext];
[person setValue:#"first name A" forKey:#"firstName"];
[person setValue:#"last name B" forKey:#"lastName"];
[person setValue:#"123" forKey:#"mobile"];
I got this error
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<NSManagedObject 0x82abcc0> setValue:forUndefinedKey:]: the entity Person is not key value coding-compliant for the key "mobile".
If i do this
NSManagedObject *person = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:self.managedObjectContext];
[person setValue:#"first name A" forKey:#"firstName"];
[person setValue:#"last name B" forKey:#"lastName"];
NSManagedObject *contactNumber = [NSEntityDescription insertNewObjectForEntityForName:#"ContactNumber" inManagedObjectContext:self.managedObjectContext];
[contactNumber setValue:#"123" forKey:#"home"];
[contactNumber setValue:#"456" forKey:#"iPhone"];
[contactNumber setValue:#"789" forKey:#"mobile"];
[contactNumber setValue:#"111" forKey:#"work"];
[contactNumber setValue:#"112" forKey:#"other"];
its working fine, but how data got related to each other in two entities?
You don't need to subclass.
You just need relations.
Make a relation on Person named contactNumbers that points to the ContactNumber entity, and a matching relation on ContactNumber entity called person that points back to person.
The contactNumber relation should be one to many (because one person has many contacts), while the relation from ContactNumber going back to Person should be one to one (because each contact entry will appear in only one person).
When you get a Person entity then, you'll be able to get a set of ContactNumber objects from the contactNumbers property, and then look through elements in the set. Or you can search ContactNumber entities where the person ID matches the person relation going back to Person.
Followup: Your first code does not work because you cannot reference a single instance through a set.
Your second section of code is fine, just add the line
contactNumber.person = person;
Then you can access the set of contacts from a person at any time with:
NSSet *contacts = person.contactNumbers;
And get the contact number you want. You cannot go directly from a person to one contact without some very tricky keypath stuff.
I would EXTREMELY STRONGLY suggest using Mogenerator to generate data objects after you set up the model, it makes the data clearer as to how you can use it. Using the setKey mechanism with Core Data is UTTERLY INSANE.
You do not need to do any subclassing at all to read and write Core Data relationships (or any other values for that matter). In Core Data, a relationship is represented as an NSMutableSet. Here is some sample code to show you how to retrieve this set and modify it:
// Assuming myPerson is an NSManagedObject instance of the Person entity
NSMutableSet *contactNumbers = [myPerson mutableSetValueForKey:#"contactNumbers"];
// Add numbers normally by calling the set's addObject property
[contactNumbers addObject:myNewNumber];
//etc.
I would also highly recommend taking a look at the Sensible TableView framework if you haven't already done so. The framework will automatically generate the UI for you Core Data entities, including all relationships. It will also automatically handle adding/removing new relationship objects. I myself wouldn't imagine going back to doing stuff manually again without it. Good luck!

Working with several NSManagedObject

My question is very simple. I have something like 10 different entities in CoreData, all with the same attributs (name, description ...). To access these attributes i doing in this way:
MyEntity *entity=...;
MyEntity2 *entity2=...;
...
MyEntity10 *entity10=...;
[self myfunction:AnEntity];
After I send a random object to a function
-(void)myfunction:(id)myentity
And here i would like to use a variable which can access the entity attributes whether it's a king of class MyEntity or MyEntity2... The problem is that i can't do:
id myobject=myentity;
NSLog(#"%#", myobject.name);
If someone have a solution to avoid testing the kind of class of the object :)
Thanks !
If you have 10 different entities, I think it's time to move to NSManagedObject subclasses. Then you can define a protocol that encompasses all of the shared attributes, and declare that your NSManagedObject subclasses comply with that protocol. Then your call becomes
-(void)myfunction:(id<SharedAttributesProtocol>)myObject
{
NSLog(#"%#", myObject.name);
}
You mentioned "description" as one of your shared attributes. The -description method is already defined, so you probably want to choose another name for that attribute.
This disadvantage of using a parent NSEntity for the common attributes is that you end up with one very wide table. This table has all of the common attributes, but also has all of the distinct attributes for each of the subentities. Depending on the size of your objects, this will be a performance hit under iOS, although it's not so awful on OS X.
In fact you could call
[myobject valueForKey:#"name"]
or even
[myobject name]
in your function, because the methods are resolved at runtime. If myobject has a "name" attribute, this will work, otherwise it will crash at runtime.
A cleaner solution would be to define one "parent entity" MyEntity with the common attributes name, description etc. Then you can define subentities MyEntity1, MyEntity2, ... which have MyEntity as "Parent Entity". These subentities inherit all attributes/relationships of the parent entity, and can have additional attributes and relationships.
The corresponding managed object subclasses are then subclasses of the MyEntity class. Your function could look like this:
- (void)myfunction:(MyEntity *)myentity
{
NSLog(#"%#", myentity.name);
}
and you can call it with instances of any of the subclasses:
MyEntity1 *myentity1 = ...;
[self myfunction:myentity1];
MyEntity2 *myentity2 = ...;
[self myfunction:myentity2];

Many - Many Relationship Core Data

I have a condition where I have many-many for my two entities:
A category can have many places, in other hand, places can be also
under many categories
The data are successfully stored in my sqlite, but when I check the structure of both entites in sqlite manager software, the relationship between two of it is gone.
Usually, when I declare 1..* , for example:
Entity B has one entity A
Place is under one category
Category will become an attribute in Place, where I can use it for later use:
Category *category = (Category*)place.category;
NSString *catId = category.catId;
and it should returns a value of catId if there is value there.
But in my many-many case, I can't even get the catId, it crashes like this:
-[_NSFaultingMutableSet catID]: unrecognized selector sent to instance
Do I miss a concept here? I believe core data supports many to many relationship as I've read it in some web. Thanks!
You have a 'to-many' relationship from Place->Category, so place.category is a NSMutableSet object not a Category object
Try this
NSMutableSet *categorySet = place.category;
foreach (Category *category in categorySet)
{
NSString *catId = category.catId;
NSLog(#"catId: %#", catId);
}

How can I save an object that's related to another object in core data?

I'm having difficulty with a one to one relationship. At the highest level, I have a one to many relationship. I'll use the typical manager, employee, example to explain what I'm trying to do. And to take it a step further, I'm trying to add a one to one House relationship to the employe.
I have the employees being added no problem with the addEmployeesToManagereObject method that was created for me when I subclassed NSManagedObject. When I select an Employee in my table view, I set the currentEmployee of type Employee - which is declared in my .h.
Now that I have that current employee I would like to save the Houses entities attributes in relation to the current employee.
The part that I'm really struggling with is setting the managedObjectContext and setting and saving the houses attributes in relation to the currentEmployee.
I've tried several things but here's my last attempt:
NOTE: employeeToHouse is a property of type House that was created for
me when I subclassed NSManagedObject
House *theHouse = [NSEntityDescription
insertNewObjectForEntityForName:#"House"
inManagedObjectContext:self.managedObjectContext];
// This is where I'm lost, trying to set the House
// object through the employeeToHouse relationship
self.currentEmployee.employeeToHouse
How can I access and save the houses attributes to the currentEmployee?
since House is setup as an Entity it can be considered a table within the data store. If that truly is the case, you need to setup a 1 to 1 relationship between Employee and House in your data model.
If you have already done so, then it is as simple as calling. Although I'm not as familiar with one to one relationships with Core Data as I am with to-many. In either case, try one of the following
[self.currentEmployee addHouseObject: theHouse];
or
self.currentEmployee.employeeToHouse=theHouse;
then to the save to the managedObjectContext:
NSError *error=nil;
if (![self.managedObjectContext save:&error]{
NSLog(#"Core Data Save Error: %#", error);
}
Also, I'm not sure about your particular situation, but your self.managedObjectContext should already be the same as the one pointed to by self.currentEmployee.managedObjectContext.
Good luck,
Tim

Resources