iOS / Core Data - Best way to return an array from a NSSet relationship? - iphone

I'm creating an app and I'm storing the data using core data. It works fine but I was wondering what's the best way to return a sorted array from an set ?
For example, I have an entity A (Button) that contains a list of entity B (Image). As we all know core data is storing the to-many relationship using an NSSet. But I want to keep track of the initial order so I added a property "order" for that purpose.
The problem is that in my code I need the list of images sorted so in a category file (Button+Create) I created a method like this one :
- (NSArray*)imagesArray
{
return [self.images sortedArrayUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDeacriptorWithKey:#"order" ascending:YES]]];
}
But I don't think that's the best way to do because if a need to access this list from multiple area in my code (different classes) then I need to sort the set x times ...
Is there a way to add an array (property) in my category in order to sort once and then only retrieve the property ?
What are your thoughts about that ?
Thanks

You can add properties to categories:
http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/ObjectiveC/Chapters/ocAssociativeReferences.html
Here is another example:
How to add properties to NSMutableArray via category extension?
But
Be aware that you will be responsible to observe changes (e.g. via KVO) to the initial coredata NSSet property.
So if changes occur you have to "recalculate" your property properly...
If your target was iOS 5.0 or higher, I would recommend to use Ordered Sets, but due to the fact that you are not, I could imagine using the way described in the links above

I think your approach with order property is correct. I'd rather create a method in my model something like -(NSArray *)sortedItems which would have an NSFetchRequest with appropriate sort descriptor. If you want you can cache the result - save this array in memory. Your NSManagedObject can also have properties that are not stored in the database, just in memory. But I really don't think this is necessary considering the speed and performance of Core Data.

Related

Alternative to NSFetchedResultsController?

Currently I have a UITableView with its data source being an NSFetchedResultsController. The most important thing the NSFetchedResultsController does is automatically update my table if there are any changes, via delegate methods. However, I no longer need to do a fetch to get my entity, call it "Pictures" for now. I have another entity called Folder, and folders have a relationship with Pictures, so every folder has an NSSet pictures.
So instead of fetching all pictures that belong to a certain folder, now I can just do folder.pictures, and that returns what I need, and I can assign that to an array and set that as my tableView source. However, this doesn't give me automatic table updates like an NSFetchedResultsController would.
My question is how can I have the functionality of an NSFetchedResultsController (that is, the delegate methods that automatically update my table) without executing a fetch? I don't need to fetch anymore since I have an NSSet with the desired NSManagedObjects.
What's wrong with the fetched results controller? Just keep it and use the dot notation for relationship sets as well - you get the best of both worlds.
The real advantage of the fetched results controller is actually hidden. It will fetch your objects (folders) alright - but maybe it will not fetch all the relationship attributes (pictures). This is called faulting. It means that core data will get the data in the background if it is needed. It is automatically optimized for speed and good memory usage. For example, the potentially huge array of your datasource will not have to be all in memory at once, something that is unavoidable with an array.
Thus, you really do not want to get rid of the FRC. She is your friend. Stay faithful to her. ;-)

Syncing up Core Data with a NSMutableArray

I know this is probably a pretty basic question, but I'm working with Core Data and a UITableView. I import all the data from Core Data to various mutable arrays. When a user rearranges the items in the table, it's easy enough to swap the items in the mutable arrays I obtained from core data earlier, but is there an easy way to sync the new mutable array with the existing core data? Any help is appreciated!
I think your question is actually how to persist the order of the data in your table view rows in Core Data.
The answer is you have to introduce a new integer / NSNumber attribute to keep track. I have done this in a simple app managing a to-do list.
Use a NSFetchedResultsController. It greatly simplifies using a table view and Core Data together. No need worrying about intermediary arrays.
Lion added support for ordered to-many relationships, which is much easier than storing the indices within the entities. If you're targeting Lion you can mark the relationship Ordered and access it using a new class NSOrderedSet, which will persist its order, like an array.

How to efficiently get all valid values of an attribute from an NSManagedObject?

I have an iPhone app with a Core Data object that has a "color" attribute. I'd like to get a list of all the values for color that have been saved. A simple SQL statement SELECT DISTINCT(color) FROM myObjectTable would easily do this. How can I do this in Core Data without loading all the objects (of which there may be thousands) into an in-memory NSSet?
You can:
1) set NSFetchRequest's requestType to NSDictionaryResultType
2) "setPropertiesToFetch" in NSFetchRequest to fetch only the property instead of the whole object.
I haven't found a good solution to this yet either. But you can as Nevin suggests get specific attributes instead of the entire managed objects.
See Fetching Specific Values from Apple's documentation for more detail.
You will get a NSArray of NSDictionary objects that you can then loop through, extracting the color values that you are looking for.

Setting a limit to a fetched property in Core Data

I have a one to many relationship between two objects, lets call them Gallery and Images.
Each Image belongs to a Gallery and each Gallery has many Images.
I would like to add a fetched property to my Gallery model which would return one and only one Image object.
Is there a way to do this with fetched properties?
For a fetched property, a predicate is your only option.
See the Predicate Programming Guide - Aggregate Operations section. You'll want to use array[FIRST].
Note, you'll likely get a different image each time, since there is no support for ordered sets in Core Data. You'd normally get around this by maintaining your Images' sort order in a "sortOrder" key and setting sort descriptors on your fetch, but I don't think it's possible to give sort descriptors on a fetched property.
Update for Lion: Support for ordered sets has been added to Core Data in 10.7 and above, making an extra "sortOrder" attribute unnecessary for apps targeting 10.7 and up.
A fetched property is represented by the NSFetchedPropertyDescription class. You can modify properties in code up until the point when the managed object model is actually used. So, in the code that loads up your managed object model, you can find your fetched property description and replace the fetch request with something that better matches what you're trying to do. You should be able to set a fetch limit on it in this way.

Is there a bulk update operation for all entities in Core Data?

On the iPhone, Does Core Data have a way to bulk update a field for every instance of an entity that it's storing? Something like
update some_entities set some_count = 0 where some_count > 0
Or do I just have to instantiate every entity, set the value, then save it? (And if that's the answer, how could I do that in a single transaction, assuming the set is too large to fit in memory?)
This is not provided by Core Data, but you could use the makeObjectsPerformSelector:withObject: method of an NSSet or NSArray of your Core Data managed objects.
Pass the setter accessor as the selector and the value as the object.
For example, if the managed objects have an attribute "name" that needs to be set the same for all:
[[fetchedResultsController fetchedObjects] makeObjectsPerformSelector:#selector(setName:) withObject:#"name for all"];
You don't have to have an NSFetchedResultsController. You can use the array from an NSFetchRequest or even the NSSet from a to-many relationship among your Core Data entities.
Core Data isn't a database. If you want to bulk-update the objects, you'll have to fetch them and update the values yourself.
A good way of doing that would be to fetch, say, 100 at a time (using an NSFetchRequest with a fetchLimit set), update them, and then save the managed object context. Lather, rinse, repeat until all the objects are updated.
And, as gerry suggested, if the update you're doing is simple, you can use makeObjectsPerformSelector: to do the update in one line.
No, Core Data doesn't have a bulk update feature. If you're memory-constrained, you might consider redesigning your data model to simplify things; instead of storing an absolute count for each entity, track a master value for a group of entities and store a delta from that value per entity.
Core Data can definitely be frustrating at times for those of us used to thinking in SQL terms.