1

I am stuck on what is supposed to be a very simple thing to do: have a Core Data Entity store / display (through bindings) an image assigned as a transformable attribute.

I've read many related posts on the Stack (e.g., see here and here), but am still having trouble with it, after having developed sample code and researched other articles (e.g., see here as well as here). This is related to my earlier question, which I still have not resolved.

I created a simple doc-based Core Data App to demonstrate the problem. The Core Data managed object is called "TheEntity" and the attribute "theImageAtt." The entity as defined in Core Data is shown below (ImageValueTransformer is the NSValueTransformer):
enter image description here

I let XCode generate the NSManagedObject subclass header and implementation files (I left out the code for the "name" attribute to make it simpler):

//  TheEntity.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#import "ImageValueTransformer.h"
@interface TheEntity : NSManagedObject
@property (nonatomic, retain) NSImage * theImageAtt;
@end
-----------------------
//  TheEntity.m
#import "TheEntity.h"
@implementation TheEntity
@dynamic theImageAtt;
@end

Below are the header and implementation files for my "ImageValueTransformer." Lots of examples of this on the Stack and elsewhere (the tiff rep is arbitrary).

//  ImageValueTransformer.h
#import <Foundation/Foundation.h>
@interface ImageValueTransformer : NSValueTransformer
@end
-------------------------------
//  ImageValueTransformer.m
#import "ImageValueTransformer.h"
@implementation ImageValueTransformer
+ (BOOL)allowsReverseTransformation {return YES;}
+ (Class)transformedValueClass {
    return [NSData class];  // have also tried: return [NSImage class]; 
}
- (id)transformedValue:(id)value {    
    NSData *data = [value TIFFRepresentation]; 
    return data;
}
- (id)reverseTransformedValue:(id)value {
    NSImage *imageRep = [[NSImage alloc] initWithData:value];
    return imageRep;
}
@end

The Value Transformer can be initialized / registered by allocating an instance of it in MyDocument.m, but in the end, it doesn't matter that much as long as the transformer header is imported into the theEntity Header (see above). I have experimented with this and it does not remove the error I get below. For reference, there is earlier discussion on whether or not the value transformer needs to be registered (see the comments by CornPuff and Brian Webster).

Back to the problem at hand, a good code example from Apple is here which shows an alternative initialization of the value transformer, I tried that setup too.

Putting this into action, I have a method to load a test image and assign it to the transformable attribute in MyDocument.m (from a selected Entity instance in an NSTable):

- (IBAction)addImg:(id)sender {
    NSImage *theImg = [[NSImage alloc] initWithContentsOfFile:@"/Desktop/testImage.jpg"];
    //[theImageView setImage:theImg]; // as a test, this displays ok
    // "theEntities" below are defined as an IBOutlet to my Array Controller:
    [theEntities setValue:theImg forKeyPath:@"selection.theImageAtt"];
    NSLog(@"after setting the image ..."); // this never logs
}

Zeroing-in on where the code breaks, the line below:

[theEntities setValue:theImg forKeyPath:@"selection.theImageAtt"];

gives the error:

Cannot create BOOL from object <4d4d002a 0003717e 8989898a 8a8a8b8b 8b8b8b8b
8a8a8a89 89898888 88878787 8a8a8a89 89898888 88888888 88888889 89898a8a
8a8a8a8a 8b8b8b88 88888787 87898989 8a8a8a89 89898a8a 8a8c8c8c 8b8b8b8a
8a8a8888 .... and so on for the size of the Image array ...

If I comment out the said line above then my NSTable populates just fine (so the bindings and array controller seem ok), but of course with no image in the NSImageView.

As a check, the conversion code used in the Image Transformer works as expected (this is tested separately from the value transformer):

// Image to Data Conversion:
NSImage *imageIn = [[NSImage alloc] initWithContentsOfFile:@"testImage.jpg"];
NSData *imgData = [imageIn TIFFRepresentation];
NSImage *imageOut = [[NSImage alloc] initWithData:imgData];
[theImageDisplay setImage:imageOut]; 

What am I missing on this?

Community
  • 1
  • 1
Bruce Dean
  • 2,798
  • 2
  • 18
  • 30
  • have you determined whether or not the image transformer works outside of it's use in Core Data yet? i.e. transformed from the TIFF to NSData and back again? – timthetoolman Feb 23 '12 at 04:32
  • I am not sure if it matters but have you registered the value transformer in +initialize? Worth a try. What happens if you don't assign the property your own value transformer but use the standard one instead? Should also work since NSImage implements the NSCoding protocol. – Christian Kienle Feb 23 '12 at 09:57
  • **timthetoolman:** thanks for your suggestion. I have checked that the transformation works in code, but not explicitly by using a separate instance of my image transformer outside of core data. Is that what you meant? Anyway, I added the code used for a check on the image transformer at the end of my question above. Thanks. – Bruce Dean Feb 23 '12 at 18:39
  • **cmk:** thanks for your suggestion. Yes, I have registered the transformer, has no effect. This [post also discusses](http://stackoverflow.com/questions/1598600/why-is-my-transformable-core-data-attribute-not-using-my-custom-nsvaluetransform) that. See the comments by CornPuff and Brian Webster. Your also right in suggesting to use the standard transformer, but I'm having issues with that, not sure if it is being called, it should be called automatically. Either way, no image displays. – Bruce Dean Feb 23 '12 at 19:52
  • Instead of using setValue:forKeyPath of the subclass, can you try to use setAttributeName:obj ? I was reviewing a WWDC video recently that stated this would lessen the chance of errors occurring due to invalid key paths. Also the error seems to imply that the image being passed is attempting to be set on a Bool rather than a valid image/data object for the keyPath. Once you verify the correct key path type then I believe you will discover that you should point to a new key path and see the correct behavior. – Tommie C. Sep 11 '15 at 19:26

1 Answers1

0

I found that the error reported above, namely,

Cannot create BOOL from object ...

does not occur when using an Image Well or Image Cell (subclasses of NSImageView) rather than the Custom View that I was trying to write to earlier.

So for now I'm using the default value transformer rather than a custom value transformer. This is a workable solution, but academically speaking, it would be nice to know why the default Custom View led to errors when binding to a Core Data attribute (defined as transformable in the Core Date Model).

Digging into this a little further, the Image Well inherits from NSImageView, so at least one difference is that they are distinct with regard to the "editable" property (Image Wells are editable which plays well with Drag-n-Drop). So in an attempt to reconcile these two, I set my Custom View to editable, thinking that this might resolve the problem:

theImageView = [[NSImageView alloc] init];
[theImageView setEditable:YES];

But it must be something else, this does not resolve the error above. For now at least, I have a workable solution. Should others encounter this, I hope these notes are helpful!

Bruce Dean
  • 2,798
  • 2
  • 18
  • 30