0

I am learning CoreData with a sample inventory app. My Data Model has 2 entities, "Possession" and "Asset Type". Possessions have a to-one relationship to Asset Types called "assetType" and Asset Types have a to-many relationship to Possessions named "possessions". Each Possession has only one Asset Type, but Asset Types have many Possessions.

I'd like to sort a tableview into sections based on asset types. I need some help populating my data structure for this though.

Here is how I'll model my table view controller

NSArray for table sections each section object in the array will be an NSDictionary with 2 keys, @"Header" - which will be the Asset Type, and @"Possessions" which is an array of possessions of that asset type.

I can handle building my table view from this structure no problem. I can also handle fetching the Asset Types from CoreData into the header key of my dictionary. Where I am stumped is how do I take advantage of the to-many relationship that asset types have to possessions so I can get the possessions into an array for the dictionary.

How do I get each Asset Type's possessions into an array?

Do I need to make a fetch request with a predicate to fetch possessions that have the matching asset type, or is there a simpler way? - Seems that would be inefficient as it would have to check all possessions for a match!?!?!?!?

Is the to-many relationship "possessions" of AssetType entity an accessible property of the AssetType entity? If so how do I access it? What does it return?

I am not looking for free code, but I am willing to post mine if needed. What I'd really like is to know the best way to solve this problem and maybe pointed to helpful info online to accomplish this. I am not a complete newb, but I am still having trouble wrapping my head around CoreData and thus far the Apple docs are still pretty confusing to me on this subject. I am appreciative for any help offered.

vichudson1
  • 881
  • 1
  • 10
  • 21

2 Answers2

1

If you've created your AssetType class with Xcode default NSManagedObject template (see image)

File/New/File.../Core Dats/NSManagedObject subclass

you should have something like that in your AssetType.h:

@interface AssetType : NSManagedObject

@property (nonatomic, retain) NSSet *possessions;

@end

@interface AssetType (CoreDataGeneratedAccessors)

- (void) addPossessionsObject:    (Possession *) value;
- (void) removePossessionsObject: (Possession *) value;
- (void) addPossessions:          (NSSet *) value;
- (void) removePossessions:       (NSSet *) value

@end

So if you have an AssetType object, you can access all Possession objects related to it via the possessions property. It is not ordered by default though, so to access these objects as an array, you will need to call the -allObjects method of the set which returns the objects within the set as an array and then probably sort these objects somehow.

Edit:

Also, even if you did not create the NSManagedObject subclass that way and it does not have the possessions property implemented yet, you could add this property to the class in the just same way as stated in the example above and implement it within the AssetType.m as @dynamic possessions.

Egor Chiglintsev
  • 1,252
  • 10
  • 9
  • Thanks for your answer. So far I have Possession as a subclass of NSManaged Object, but simply fetching the Asset Types Directly from the object context, as thus far it was only used for a label in a possessionDetailViewController. Let me make sure I'm understanding you correctly. Create a subclass for AssetType, fetch the AssetType objects into an array to use to make my dictionaries like this. Looping through that array [..add the label to my header key [assetTypevar label]...] then something like this for my possessions array pArray = [assetTypevar possessions] allObjects] – vichudson1 Apr 11 '12 at 16:35
  • 1
    Yes, `pArray = [[assetTypevar possessions] allObjects]` should work. The to-many relationships are usually represented by NSSet objects (except in iOS 5 Apple has introduced ordered to-many relationships which are represented as NSArrays directly, but these are not supported in the previous versions of the iOS) – Egor Chiglintsev Apr 11 '12 at 16:43
  • Thank you! One last question. Will I need to regenerate my possession classes too after I make the AssetType Subclass or will they be fine? – vichudson1 Apr 11 '12 at 16:46
  • Possession class should be fine since generation of the AssetType class only creates the AssetType.h and AssetType.m files and does not touch anything else. – Egor Chiglintsev Apr 11 '12 at 16:49
0

from what I'm reading I suspect this is much easier than you might think.

It sounds to me like you have envisaged using a dictionary data structure with for use in your UITableController's tableView:cellForRowAtIndexPath: method, when you probably don't need to use one at all.

When you Table view controller instantiates, (viewDidLoad: is probably the most appropriate place) simply retrieve your assets with code like this:

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Asset" inManagedObjectContext:context];
[fetchRequest setEntity:entity];

NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
    // handle errors here
}

[fetchRequest release];

If necessary assign the fetchedObjects array to an instance variable of type NSArray so you can refer to it in multiple places in the UITableViewController class managing the table. Then after the above code you can do something like this:

self.assets = fetchedObjects;

If you have not created a custom subclass for your asset NSManagedObject, then each object in the array will be of type NSManagedObject and you will need to read it into a variable of that type.

When you need to the list of possessions related to an asset, it's already there in each asset object in your array (that's the beauty of CoreData, if it's not actually instantiated in memory, the moment you try to access the property, CoreData will automagically instantiate it for you).

So let's say you have not defined a custom class for your NSManagedObject then your array objects will be of that type. Assign one of the asset objects in your array to a local variable named (appropriately) asset,

For example, this will assign the object at index 0 to a local variable.

NSManagedObject *asset = [self.assets objectAtIndex:0];

You get can get your set of possessions with the code:

NSSet *assetPossessions = [asset valueForKey:"@possessions"];

(presuming the relationship defined in the model editor in xCode has been named "possessions")

Alternatively If you generated your own custom subclass for the Asset NSManagedObject, then, this might be a better example:

MyManagedAssetObject *asset = [self.assets objectAtIndex:0];
NSSet *assetPossessions = asset.possessions;

Note in this example, I have presumed you are not using a NSFetchedResultsController. It is actually even easier to manage a table using one, however as is often the case with programming it is easier only once you know that "just one more thing" and so (paradoxically) complicates the answer. Since you didn't mention it, I have assumed you are not using it.

Hope this helps. Paul

TheBasicMind
  • 3,585
  • 1
  • 18
  • 20
  • oh I should have mentioned you may want to order the fetch request - check out the Xcode code template for doing that (in the code snippet library). Also if using iOS 5.0 you might want to choose to choose to store possessions in assets as an NSOrderedSet - so the order is stored in the CoreData object. If you need more flexibility around ordering, re-order the NSSet assetPossessions by first converting the NSSet (which is by nature unordered) to NSArray with: NSArray *possessionSetAsArray = [assetPossessions allObjects]; – TheBasicMind Apr 11 '12 at 17:06
  • Thanks for your feedback. I am not using the NSFetchedResultsController. I've been using a book written prior to that, I believe it was new for iOS5. I chose the array of dictionaries structure because it allowed me to have simpler code in my tableview where my data model is already grouped into sections under headers. I just load the sections array at app launch and refresh as needed during runtime for edits, deleting, removing, or editing Possessions and Assets. I just use my index path section to get the right dictionary and the row to get what I need from it's array. – vichudson1 Apr 11 '12 at 17:14