In my Core Data schema, I have a 'transformable' attribute in an entity, which is using a NSValueTransformer
, the purpose of which is to convert a UIImage into NSData with some compression. From this attribute, I had recently started getting these warnings about using NSKeyedUnarchiveFromData
:
'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release
So I read about this (link) and figured I'd need to change the NSValueTransformer
to using NSSecureUnarchiveFromDataTransformer
... but after making the changes (as best as I understood them from the article) I can't get it to work, and now the app crashes when the attribute is accessed.
This is the existing NSValueTransformer
:
@interface ImageToDataTransformer : NSValueTransformer {
}
@implementation ImageToDataTransformer
+ (BOOL)allowsReverseTransformation {
return YES;
}
+ (Class)transformedValueClass {
return [NSData class];
}
- (id)transformedValue:(id)value {
// for our smaller views this uses much less data and makes for faster syncing
NSData *compressedData = UIImageJPEGRepresentation(value, 0.4);
return compressedData;
}
- (id)reverseTransformedValue:(id)value {
UIImage *uiImage = [[UIImage alloc] initWithData:value];
NSData *data = (NSData *) value;
//NSLog(@"reverseTransformedValue: image size: %@", [NSByteCountFormatter stringFromByteCount:data.length countStyle:NSByteCountFormatterCountStyleFile]);
return uiImage;
}
So I changed it to subclass from NSSecureUnarchiveFromDataTransformer
instead, and added the following to the implementation:
+ (NSArray<Class> *)allowedTopLevelClasses {
return @[[ImageToDataTransformer class]];
}
+ (void)setValueTransformer:(nullable NSValueTransformer *)transformer forName:(NSValueTransformerName)name {
NSLog(@"ImageToDataTransfer: calling setValueTransformer");
[NSValueTransformer setValueTransformer:transformer forName:name];
}
+ (NSArray<NSValueTransformerName> *)valueTransformerNames {
return @[@"ImageToDataTransformerName"];
}
Then, before the Core Data persistent store is accessed, I 'register' the transformer:
[ImageToDataTransformer setValueTransformer: [[ImageToDataTransformer alloc] init] forName:@"ImageToDataTransformerName"];
Now the warning is gone, but the app crashes when the image is read:
-[__NSCFData _rasterizedImage]: unrecognized selector sent to instance 0x7ff53c108800
If I change the transformer back to NSValueTransformer
, it works fine. So I'm not sure if I'm missing something from the implementation, or I have misunderstood the premise of 'NSSecureUnarchiveFromDataTransformer'. Would love to know what I can do to fix this.
I don't think you need to override the
+valueTransformerNames
and the+setValueTransformer:forName:
class methods.It seems from the documentation that they should not be overriden, since the first class method will return the value transformer names that are actually registered at the time of the call. And the second method is how you actually register your value transformer. So you should remove these two overriden methods.
You can register your value transformer in the
+load
class method of your value transformer before anything else happens in your app, as follows:Then you override
+allowedTopLevelClasses
to return the allowed top-level transformable classes, which is just the UIImage class as follows:Then in your Core Data schema put
UIImage
as the custom class for the transformable attribute, andImageToDataTransformer
as the value transformer name.Let us know if it works.