Best practice? - Array/Dictionary as a Core Data Entity Attribute

75,404

Solution 1

There is no "native" array or dictionary type in Core Data. You can store an NSArray or an NSDictionary as a transformable attribute. This will use the NSCoding to serialize the array or dictionary to an NSData attribute (and appropriately deserialize it upon access). The advantage of this approach is that it's easy. The downside is that you can't query into the array or dictionary (it's stored as a BLOB in the data store) and if the collections are large, you may have to move a lot of data to/from the data store (if it's an SQLite data store) just to read or modify a small part of the collection.

The alternative is to use Core Data to-many relationships to model the semantics of the array or dictionary collection. Arrays are easier, so lets start with that. Core Data to-many relationships are really modelling a set, so if you need array-like functionality, you have to either sort the set (using a fetched property is a convenient way to do this) or add an extra index attribute to the entity that stores the array items and manage the indexes yourself. If you are storing a homogeneous array (all entries are the same type), it's easy to model the entity description for the array entities. If not, you'll have to decide whether to use a transformable attribute to store the item data or create a family of item entities.

Modeling a dictionary will likely require a to-many relationship to a set of entities that stores a key and a value. Both key and value are analogous to the item entity for the array, described above. So they could either be native types (if you know them ahead of time), a transformable attribute or a relationship to an instance from a family of type-specific entities.

If this all sounds a bit daunting, it is. Shoehorning arbitrary data into a schema-dependent framework like Core Data is tough.

For structured data, like addresses, it's almost always easier to spend the time modeling the entities explicitly (e.g. an attribute for each part of the address). Besides avoiding all the extra code to model a dictionary, this makes your UI easier (bindings will "just work") and your validation logic etc. much clearer since much of it can be handled by Core Data.

Update

As of OS X 10.7, Core Data includes an ordered set type which can be used in place of an array. If you can target 10.7 or later, this is the best solution for ordered (array-like) collections.

Solution 2

I had a similar issue. In my case, I wanted to map an array of strings. I followed Barry's advice and finally got it working. Here is what some of the code looks like (which will hopefully clarify things for anyone else who runs into this)...

My Entity looks something like this:

@interface AppointmentSearchResponse : NSManagedObject
@property (nonatomic, retain) NSSet *messages;
@end

My Manage Object Model Code (Core Data) code looks something like this:

NSEntityDescription *entityDescription = [[NSEntityDescription alloc] init];
[entityDescription setName:@"AppointmentSearchResponse"];
[entityDescription setManagedObjectClassName:@"AppointmentSearchResponse"];

NSMutableArray *appointmentSearchResponseProperties = [NSMutableArray array];
NSAttributeDescription *messageType = [[NSAttributeDescription alloc] init];    
[messageType setName:@"messages"];
[messageType setAttributeType:NSTransformableAttributeType];
[appointmentSearchResponseProperties addObject:messageType];

[entityDescription setProperties:appointmentSearchResponseProperties];

So the key items here are:

  • I'm using an NSSet for the property type
  • I'm using NSTransformableAttributeType as the attribute type in the Core Data Managed Object Model.
Share:
75,404

Related videos on Youtube

QuangDT
Author by

QuangDT

Updated on August 01, 2020

Comments

  • QuangDT
    QuangDT almost 4 years

    I am new to Core Data. I have noticed that collection types are not available as attribute types and would like to know what the most efficient way is of storing array/dictionary type data as an attribute (e.g. the elements that make up an address like street, city, etc. does not require a separate entity and is more conveniently stored as a dictionary/array than separate attributes/fields). Thank you.

    • Daniel
      Daniel over 14 years
      Making an entity with string fields for the address is probably easier to use than a dictionary where you have to remember your keys...
  • jkp
    jkp almost 14 years
    Seconded - confirmed what I already thought but I didn't know about transformable attributes.
  • Deamon
    Deamon almost 13 years
    @Barry So I am curious, when would be the "correct" time to use transformable? Say if my entity has an array of strings, the array contains no more than 100 items and the string is an average English word, would it be okay to use transformable?
  • Barry Wark
    Barry Wark over 12 years
    @pixelfreak Use of transformable depends on how you need to use the items in the collection. If you need to query against them, or you want to be able to lazily load some or all of them, a transformable attribute won't work. If you don't need to lazy load, don't need to query and always need all of the items or none, a transformable attribute may work for you (and is certainly easy to implement).
  • Palimondo
    Palimondo over 12 years
    What Barry says is described on more detail in Core Data Programming Guide, chapter Non-Standard Persistent Attributes.
  • Echelon
    Echelon almost 12 years
    Very good answer, thank you. I needed a small, effectively constant array of strings in an existing model and had no inclination to start adding new relationships and models. I simply used a "Transformable" type in the model and declared the property to be NSArray in my NSManagedObjectModel subclass as detailed in @Palimodo's link above. And it worked seamlessly loading the array from a plist too, using setValuesForKeysWithDictionary:.
  • Kirk van Gorkom
    Kirk van Gorkom almost 12 years
    A note of caution about ordered sets: do not use them for to-many relationships with more than a couple thousand objects on the many side. If you do, saving can start to take so long that it blocks the thread.
  • Plot
    Plot over 10 years
    I don't understand about the "new ordered set". Is it an attribute ? Because i can't see it in the attribute type menu.
  • Plot
    Plot over 10 years
    Never mind i went with the relationship method :) thanks!
  • Chicowitz
    Chicowitz over 8 years
    So would you have put this code inside AppointmentSearchResponse.m within an init method?