NSSecureCoding

I’ve had it on my list to convert all my NSCoding classes to use NSSecureCoding and this recent post reminded me so I figured now is as good a time as ever.

How do you go about using secure coding? Here are the Apple docs. Unfortunately, those docs don’t give the whole picture. There are a few details you need to be aware of.

When you subclass an object that supports NSSecureCoding, if you override -initWithCoder, you must also implement +supportsSecureCoding

Even if you don’t call -decodeObject:… in your -initWithCoder: you still have to implement +supportsSecureCoding and return YES in your class, even if a superclass already did it.

When decoding collections, you must specify the classes of the objects it contains

This is actually documented elsewhere in Apple’s XPC docs. Suppose you have an NSArray. You can’t just say -decodeObjectOfClass:[NSArray class]…. That’s because when NSArray unarchives itself, it can’t specify the class of the objects within it, since they are typed as id Therefore, you need to specify the classes of those objects for it. In this case, you would use -decodeObjectOfClasses:…, specifying the classes contained by the array as well as NSArray itself.

Some objects will be disabled after secure decoding

Objects like NSPredicate and NSSortDescriptor can take in key paths or selectors making them potentially unsafe. As a result, they are disabled after being securely decoded. To re-enable them, you have to call -allowEvaluation (presumably after doing some sort of check).

You have to turn on secure coding on the coder instances

Sounds a bit obvious except that most of the time you are using the class, not instance, methods to archive/unarchive. Those do not use secure coding nor are there similar class methods for secure coding. Instead, you’ll have to create an instance, set it to secure coding and encode/decode the root object yourself. Of course, you can bundle this up into convenience methods and stick it into a category like so:

@implementation NSKeyedArchiver (NoodleExtensions)

+ (NSData *)noodleSecurelyArchivedDataWithRootObject:(id)rootObject;

{

NSMutableData *data;

NSKeyedArchiver *archiver;

data = [NSMutableData data];

archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];

[archiver setRequiresSecureCoding:YES];

[archiver encodeObject:rootObject forKey:NSKeyedArchiveRootObjectKey];

[archiver finishEncoding];

return data;

}

@end

@implementation NSKeyedUnarchiver (NoodleExtensions)

+ (id)noodleSecurelyUnarchiveObjectOfClass:(Class)classObject withData:(NSData *)data

{

NSKeyedUnarchiver *unarchiver;

unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];

[unarchiver setRequiresSecureCoding:YES];

return [unarchiver decodeObjectOfClass:classObject forKey:NSKeyedArchiveRootObjectKey];

}

+ (id)noodleSecurelyUnarchiveObjectOfClasses:(NSSet *)classes withData:(NSData *)data

{

NSKeyedUnarchiver *unarchiver;

unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];

[unarchiver setRequiresSecureCoding:YES];

return [unarchiver decodeObjectOfClasses:classes forKey:NSKeyedArchiveRootObjectKey];

}

@end

Hopefully this will make your conversion to NSSecureCoding go a little smoother.