75

I'm new to iOS. I've been trying to make an application that will store an image captured from the camera into CoreData. I now know how to store data like NSStrings, NSDate and other type but struggling to store an image. I've read so many articles saying you must write it to the disk and write to a file, but I can't seem to understand it.

The following code is the one i used to store other data to core data.

- (IBAction)submitReportButton:(id)sender
{
    UrbanRangerAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];

    managedObjectContext = [appDelegate managedObjectContext];

    NSEntityDescription *entity = [NSEntityDescription entityForName:@"PotholesDB" inManagedObjectContext:appDelegate.managedObjectContext];
    NSManagedObject *newPothole = [[NSManagedObject alloc]initWithEntity:entity insertIntoManagedObjectContext:managedObjectContext];


    [newPothole setValue:self.relevantBody.text forKey:@"relevantBody"];
    [newPothole setValue:self.subjectReport.text forKey:@"subjectReport"];
    [newPothole setValue:self.detailReport.text forKey:@"detailReport"];
//    [newPothole setValue:self.imageView forKey:@"photo"];

    NSDate *now = [NSDate date];
    //NSLog(@"now : %@", now);
    NSString *strDate = [[NSString alloc] initWithFormat:@"%@", now];
    NSArray *arr = [strDate componentsSeparatedByString:@" "];
    NSString *str;
    str = [arr objectAtIndex:0];
    NSLog(@"now : %@", str);

    [newPothole setValue:now forKey:@"photoDate"];
    [newPothole setValue:self.latitudeLabel.text forKey:@"latitude"];
    [newPothole setValue:self.longitudeLabel.text forKey:@"longitude"];
    [newPothole setValue:self.addressLabel.text forKey:@"streetName"];
    [newPothole setValue:streeNameLocation forKey:@"location"];


    NSError *error;
    [managedObjectContext save:&error];

    UIAlertView *ll = [[UIAlertView alloc] initWithTitle:@"Saving" message:@"Saved data" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];

    [ll show];
} 
rdurand
  • 7,119
  • 3
  • 36
  • 70
Sipho Koza
  • 862
  • 1
  • 9
  • 20
  • 1
    Your image is probably stored into an `UIImage` object. If so, you can convert it to a `NSData` object with this : `NSData *imageData = [NSData dataWithData:UIImagePNGRepresentation(myUIImage)];` I believe storing `NSData` objects in `CoreData` is not really hard. – rdurand May 22 '13 at 07:29
  • 8
    IMO, storing binary data in a database is generally inadvisable. It will be slower to retrieve than reading from disk and since Core Data will probably be using SQLite internally, you will effectively be storing files inside a file (the SQLite DB), causing it to expand in size very quickly and slowing DB operations considerably. I would just store the path in the DB and then save the images to disk. – dandan78 May 22 '13 at 07:33
  • I agree with dandan78. It's unwise to save the images to the store. You should definitely save the path to the image instead. – Mikael May 22 '13 at 07:46
  • You can get the data of the image useing `UIImagePNGRepresentaion` or UIImageJPEGrepresentation and save the data. When you fetch the data you creat the image using `+imageWithData:` . Of course you can save them to files and just store the file name. I did both. Give me a minute and I'll look for some code sniplets. – Hermann Klecker May 22 '13 at 07:58
  • If you decide to store your images in the Core Data database, you can choose the "Store in External Record File" option in the model editor, so that Core Data can decide to store large binary data in a separate storage for performance reasons. – Guillaume May 22 '13 at 08:12
  • [swift implementation](http://stackoverflow.com/a/27996685/4263818) – R Menke Jan 17 '16 at 20:45
  • 1
    Get over it. You can store images in Core Data depending on the size. If they're under 100k compressed they don't even have to be in an external file. We find the performance is just fine. – RegularExpression Aug 23 '16 at 05:45

7 Answers7

173

You can store images in Core Data using the Binary Data attribute type. However you should be aware of a few things:

  • Always convert your UIImage to a portable data format like png or jpg For example:

    NSData *imageData = UIImagePNGRepresentation(image);

  • Enable "Allows external storage" on this attribute description Core Data will move the data to an external file if it hits a certain threshold. This file is also completely managed by Core Data, so you don't have to worry about it.

  • If you run into performance issues, try moving the Binary Data attribute to a separate entity.

  • You should abstract the conversion to NSData behind the interface of your NSManagedObject subclass, so you don't have to worry about conversions from UIImage to NSData or vice versa.

  • If your images are not strongly related to the entities in your model, I would suggest storing them outside of Core Data.

Premal Khetani
  • 3,167
  • 1
  • 22
  • 56
jansenmaarten
  • 1,883
  • 1
  • 10
  • 6
  • For "You should abstract the conversion to NSData behind the interface of your NSManagedObject subclass, so you don't have to worry about conversions from UIImage to NSData or vice versa." could you elaborate on how to do that? – Doug Smith Dec 02 '13 at 20:39
  • 8
    Create one method to set the image - (void)setImage:(UIImage *)image; Probably in a Category. In that method, use UIImagePNGRepresentaion or UIImageJPEGrepresentation so that you only have to do it once (you don't want to save as PNG and try to convert into JPEG). – Kevin Dec 02 '13 at 23:52
13

In xcdatamodelId subclass declare image entity as NSData... you can't use UIImage format because image data is in binary format.

@property (nonatomic, retain) NSData *imag;

In Implementation file.. convert UIImage to NSData

UIImage *sampleimage = [UIImage imageNamed:@"sampleImage.jpg"];

NSData *dataImage = UIImageJPEGRepresentation(sampleimage, 0.0);

Then finally save it

[obj setValue:dataImage forKey:@"imageEntity"]; // obj refers to NSManagedObject
James Zaghini
  • 3,701
  • 3
  • 42
  • 57
Jemythehigh
  • 583
  • 5
  • 12
7

For Swift 5 and Swift 4.2

  1. convert UIImage to Data:

    let imageData = image.jpegData(compressionQuality: 1.0)
    
  2. save to CoreData:

    let object = MyEntity(context: managedContext)   //create object of type MyEntity
    object.image = imageData                         //add image to object
    
    do {
        try managedContext.save()                   //save object to CoreData
    } catch let error as NSError {
        print("\(error), \(error.userInfo)")
    }
    
nomnom
  • 726
  • 9
  • 18
4
- (void)imageChanged:(UIImage*)image{

    if (self.detailItem) {
        [self.detailItem setValue:UIImagePNGRepresentation(image) forKey:kSignatureImage];
    }
}

In this a very brief example. self is a view controller using core data and self.detailItem is the usual NSManagedObject. In that project I did not create model classes for the entities but strictly use the KVC pattern to access the attributes. As you might guess, the attribute is named "signatureImage" which I had defined in an #define constant kSignatureImage.

This is where the image is restored from core data:

self.signatureCanvas.image =  [UIImage imageWithData:[self.detailItem valueForKey:kSignatureImage]];

Again, self is a view controller, signatureCanvas is a UIImageView subclass and .image is its regular image property inherited from UIImageView. detailItem, again, is the usual NSManagedObject.

The example is taken from a project I am currently working on.

There are pros and cons for storing large data objects like images in core data or having them separated in files. Storing them in files means that you are responsible for deleting them when the related data objects are deleted. That provides coding overhead and may be volatile for programming errors. On the other hand, it may slow down the underlying database. I have not yet enough experience with this approach that I could share with respect to performance. In the end, the disk storage occupied is about the same size in total.

Hermann Klecker
  • 13,792
  • 4
  • 45
  • 69
1

Few links which might help you to get through this

https://stackoverflow.com/a/3909082/558000

Save and Retrieve of an UIImage on CoreData

http://ootips.org/yonat/uiimage-in-coredata/

Community
  • 1
  • 1
Devang
  • 320
  • 3
  • 12
1

It is possible to store images in Core Data as UIImage. Although this is not considered to be good practice as Databases are not meant for storing files. We don't use core data to store the images directly instead we use the file path to where the image data is stored on your phone.

Anyways, the best method I found out while developing a side-project is demonstrated below

  1. Set the entity codeGen to Manual/None

  2. Select the attribute you want to be of Image type and choose transformable as it's type

  3. Enter Custom class identifier as UIImage

  4. Go to editor and select Create NSManagedObject Subclass

  5. The Process will create 2 swift files depending on the number of your entities(I only had 1 entity). Now, select the <EntityName> + CoreDataProperties.swift file and import UIKit

If, on clicking Jump to Definition for the @NSManaged public var UIImage definition your work is done.

Perform actions on the entities just like you would and you will be able to fetch, save, edit the image attribute by down-casting <EntityName>?.value(forKey: "<attributeName>") as? UIImage.

I had to use the entity as NSManagedObject type and was for some reason not able to access the image directly from the created subclass.

0

I don't know why you want to store image in core data, while there is other persistent storage available in iOS. If you just want to store image for cache purpose, you can store it in document directory or cache directory. Happy Coding...

Please refer below URL, How to store and retrieve images in document directory.

http://www.wmdeveloper.com/2010/09/save-and-load-uiimage-in-documents.html

Saving image to Documents directory and retrieving for email attachment

How do I create a temporary file with Cocoa?

Community
  • 1
  • 1
Mangesh
  • 2,171
  • 3
  • 21
  • 47
  • 1
    Lots of good reasons. You may want to copy your database and send it to a different device. Lots easier if it is sitting in a sqlite store. And overall it is just easier to manage without a lot of crappy code to keep maintained. – RegularExpression Aug 23 '16 at 05:47