NSSecureCoding: Writing UIView to disk

299 Views Asked by At

Is it possible to write a UIView to disk using NSSecureCoding. The below code results in an error.

NSData *data = [NSKeyedArchiver archivedDataWithRootObject:object requiringSecureCoding:YES error:&error];

Error: The data couldn’t be written because it isn’t in the correct format.

We also tried the following:

NSMutableData *data = [NSMutableData data];
NSKeyedArchiver  *archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding:YES];
[archiver encodeObject:view forKey:@"view"];
[archiver finishEncoding];

Error: This decoder will only decode classes that adopt NSSecureCoding. Class 'UIView' does not adopt it.

1

There are 1 best solutions below

2
On

NSSecureCoding, in addition to NSCoding requirements, simply requires the class to implement a class function +(BOOL)supportsSecureCoding. UIView already supports NSCoding and it seems like it might be an oversight that it doesn't already conform to NSSecureCoding; the Xcode debugger issues warnings about non-NSSecureCoding serialization calls going away in the distant future.

You can add the class function to UIView using a category:

@interface UIView(SecureCoding)<NSSecureCoding>
@end

@implementation UIView(SecureCoding)
+ (BOOL)supportsSecureCoding {
    return TRUE;
}
@end

So as pointed out in the comments, this doesn't mean that you'll be able to deserialize with the NSKeyedUnarchiver because it appears that UIViews weren't intended to be serialized that way. I'm guessing the main reason they support serialization is for xibs/nibs/storyboards. Here's an example of UIView serialization that DOES work, but uses private APIs, so it's for illustrative purposes only:

Add declarations to access unpublished APIs:

/* Warning: Unpublished APIs!*/
@interface UINibEncoder : NSCoder
- initForWritingWithMutableData:(NSMutableData*)data;
- (void)finishEncoding;
@end

@interface UINibDecoder : NSCoder
- initForReadingWithData:(NSData *)data error:(NSError **)err;
@end

Serialization/Deserialization:

/* This does NOT work */
NSKeyedArchiver  *archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding:NO];
[archiver encodeObject:object forKey:@"view"];
[archiver finishEncoding];
data = [archiver encodedData];


NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:data error:&error];
/* error: 'UIBackgroundColor' was of unexpected class 'UIColor' */
data = [unarchiver decodeObjectForKey:@"view"];


/* This DOES work, but don't use it in an app you plan to publish */
NSMutableData *mData = [NSMutableData new];
UINibEncoder *encoder = [[UINibEncoder alloc] initForWritingWithMutableData:mData];

[encoder encodeObject:object forKey:@"view"];
[encoder finishEncoding];

UINibDecoder *decoder = [[UINibDecoder alloc] initForReadingWithData:mData error:&error];
NSObject *myView = [decoder decodeObjectForKey:@"view"];