How to store an image in core data

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];
} 

Solution 1:

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.

Solution 2:

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

Solution 3:

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)")
    }
    

Solution 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.