33

I am developing an application which demands around 100 images or maybe more to be pre-inserted into the Core Data database along with other related information.

Now I can easily add other data by just writing a few lines of code but for UIImages I am unsure how to do it without writing a lot of code. I was wondering: is there anyway to do this easily, or if there isn't what's the best way to achieve this with the least amount of effort.

Also, is it okay to store images in a Core Data database or should we only only save the addresses of images on the local file system?

Brad Larson
  • 170,088
  • 45
  • 397
  • 571
Asad Khan
  • 11,469
  • 13
  • 44
  • 59

3 Answers3

57

Storing images within a Core Data database is pretty easy to do. You just need to mark your image attribute as a transformable one and create a subclass of NSValueTransformer. Within that subclass, add code like the following:

+ (Class)transformedValueClass 
{
    return [NSData class]; 
}

+ (BOOL)allowsReverseTransformation 
{
    return YES; 
}

- (id)transformedValue:(id)value 
{
    if (value == nil)
        return nil;

    // I pass in raw data when generating the image, save that directly to the database
    if ([value isKindOfClass:[NSData class]])
        return value;

    return UIImagePNGRepresentation((UIImage *)value);
}

- (id)reverseTransformedValue:(id)value
{
    return [UIImage imageWithData:(NSData *)value];
}

For your transformable attribute, specify this subclass's name as the Value Transformer Name.

You can then create an NSManagedObject subclass for the entity hosting this image attribute and declare a property for this image attribute:

@property(nonatomic, retain) UIImage *thumbnailImage;

You can read UIImages from and write UIImages to this property and they will be transparently changed to and from NSData to be stored in the database.

Whether or not to do this depends on your particular case. Larger images probably should not be stored in this manner, or at the least should be in their own entity so that they are not fetched into memory until a relationship to them is followed. Small thumbnail images are probably fine to put in your database this way.

Brad Larson
  • 170,088
  • 45
  • 397
  • 571
  • 51
    Just a follow-up for anyone checking this out now: UIImage now conforms to NSCoding in iOS 5. If you're able to target iOS 5 and later, you can just set the attribute and Transformable and be done. You can also check "Allows External Storage" to cause larger images to be saved outside of your Core Data store automatically. – atticus Feb 06 '12 at 23:51
  • The code that's automatically generated by CoreData has id rather than UIImiage * as the attribute. Is this normal? Are we supposed to change that? – user4951 Oct 08 '12 at 09:05
  • 1
    @JimThio - Right, that's the default. Because I know that I will be setting a UIImage to this transformed property, I change the type to UIImage. This helps keep my code clear and provides a sanity check if I try to use this property in the wrong way. – Brad Larson Oct 08 '12 at 14:22
  • the default is id and I manually change that to UIImage. Is this the way it's intended? – user4951 Oct 08 '12 at 14:35
  • 1
    In response to what atticus has said, I have found that UIImage no longer conforms to NSCoding — instead, it conforms to NSSecureCoding. Using Transformable with no NSValueTransformer set will cause images to be read incorrectly. @BradLarson's solution, however, does continue to work. i.e. you must set the NSValueTransformer. – fatuhoku Aug 07 '14 at 15:06
  • "...should be in their own entity so that they are **not fetched into memory until a relationship to them is followed**" +1; I think your answer is the only place this critical, inconvenient side effect is ever mentioned. – SG1 Aug 13 '14 at 19:49
11

A good example of the image transformer as described above is in the iPhoneCoreDataRecipes demo application.

Bjinse
  • 1,339
  • 12
  • 25
  • 1
    Here is the current link to the example: https://developer.apple.com/library/content/samplecode/iPhoneCoreDataRecipes/Introduction/Intro.html – Ing. Ron Nov 11 '16 at 21:19
9

Apple does provide some advice around BLOBs: Large Data Objects (BLOBs)

If your application uses large BLOBs ("Binary Large OBjects" such as image and sound data), you need to take care to minimize overheads. The exact definition of "small", "modest", and "large" is fluid and depends on an application's usage. A loose rule of thumb is that objects in the order of kilobytes in size are of a "modest" sized and those in the order of megabytes in size are "large" sized. Some developers have achieved good performance with 10MB BLOBs in a database. On the other hand, if an application has millions of rows in a table, even 128 bytes might be a "modest" sized CLOB (Character Large OBject) that needs to be normalized into a separate table.

In general, if you need to store BLOBs in a persistent store, you should use an SQLite store. The XML and binary stores require that the whole object graph reside in memory, and store writes are atomic (see “Persistent Store Features”) which means that they do not efficiently deal with large data objects. SQLite can scale to handle extremely large databases. Properly used, SQLite provides good performance for databases up to 100GB, and a single row can hold up to 1GB (although of course reading 1GB of data into memory is an expensive operation no matter how efficient the repository).

A BLOB often represents an attribute of an entity—for example, a photograph might be an attribute of an Employee entity. For small to modest sized BLOBs (and CLOBs), you should create a separate entity for the data and create a to-one relationship in place of the attribute. For example, you might create Employee and Photograph entities with a one-to-one relationship between them, where the relationship from Employee to Photograph replaces the Employee's photograph attribute. This pattern maximizes the benefits of object faulting (see “Faulting and Uniquing”). Any given photograph is only retrieved if it is actually needed (if the relationship is traversed).

It is better, however, if you are able to store BLOBs as resources on the filesystem, and to maintain links (such as URLs or paths) to those resources. You can then load a BLOB as and when necessary.

Chris Gummer
  • 4,772
  • 1
  • 24
  • 17