For example, I removed NSError NSAttributedString classes from my XPC protocol, and that removed the intermittent exceptions. Why?
Both NSError and NSAttributedString allow for arbitrary ‘attachments’, and that can cause problems with secure coding. So, while they claim to support secure coding, they can’t always.
It’s easy to see this in action with NSError. At the end of this post you’ll find code for Waffle and SecureWaffle, where the latter supports secure coding. Let’s play around with that.
First, let’s encode an error with no user info:
NSError * e1 = [NSError errorWithDomain:@"WaffleDomain" code:42 userInfo:nil];
NSData * d1 = [NSKeyedArchiver archivedDataWithRootObject:e1 requiringSecureCoding:YES error:NULL];
This works, as you’d expect.
Now let’s try it with a secure waffle:
SecureWaffle * w2 = [[SecureWaffle alloc] init];
NSError * e2 = [NSError errorWithDomain:@"WaffleDomain" code:42 userInfo:@{ @"waffle": w2 }];
NSData * d2 = [NSKeyedArchiver archivedDataWithRootObject:e2 requiringSecureCoding:YES error:NULL];
This also works, because SecureWaffle support secure coding.
Finally, let’s try it with an insecure waffle:
NSError * error = nil;
Waffle * w3 = [[Waffle alloc] init];
NSError * e3 = [NSError errorWithDomain:@"WaffleDomain" code:43 userInfo:@{ @"waffle": w3 }];
NSData * d3 = [NSKeyedArchiver archivedDataWithRootObject:e3 requiringSecureCoding:YES error:&error];
This fails with an error because NSError can’t encode its user info. In the error you’ll see the text This decoder will only decode classes that adopt NSSecureCoding. Class 'Waffle' does not adopt it.
In XPC, everything has to support secure coding, so a test like this is a good place to start if you run into coding issues.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
@interface Waffle : NSObject <NSCoding>
@property (nonatomic, copy, readwrite) NSString * varnishName;
@end
@implementation Waffle
- (instancetype)init {
self = [super init];
if (self != nil) {
self->_varnishName = [@"Gloss" copy];
}
return self;
}
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
self = [super init];
if (self != nil) {
self->_varnishName = [coder decodeObjectOfClass:[NSString class] forKey:@"varnishName"];
}
return self;
}
- (void)encodeWithCoder:(nonnull NSCoder *)coder {
[coder encodeObject:self->_varnishName forKey:@"varnishName"];
}
@end
@interface SecureWaffle : NSObject <NSSecureCoding>
@property (nonatomic, copy, readwrite) NSString * varnishName;
@end
@implementation SecureWaffle
+ (BOOL)supportsSecureCoding {
return YES;
}
- (instancetype)init {
self = [super init];
if (self != nil) {
self->_varnishName = [@"Gloss" copy];
}
return self;
}
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
self = [super init];
if (self != nil) {
self->_varnishName = [coder decodeObjectOfClass:[NSString class] forKey:@"varnishName"];
}
return self;
}
- (void)encodeWithCoder:(nonnull NSCoder *)coder {
[coder encodeObject:self->_varnishName forKey:@"varnishName"];
}
@end