I'm having some trouble with NSCoding, I have this simple "Grade" class:
@interface Grade : NSObject<NSCoding>
@property (copy) NSString *name;
@property NSInteger grade;
@end
It implements NSCoding as follows:
-(void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeObject:self.name forKey:@"name"];
[coder encodeInt64:self.grade forKey:@"grade"];
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super init];
if(self)
{
self.name = [coder decodeObjectForKey:@"name"];
self.grade = [coder decodeInt64ForKey:@"grade"];
}
return self;
}
I have an NSMutableArray of these objects which I put into an archive like so:
NSArray<Grades*> *array = [NSArray arrayWithArray:self.tableContents];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject: array requiringSecureCoding:NO error: nil];
NSLog(@"archived data: %@", data);
This is working fine but the problem is when I try to extract from this archive:
NSError *error = nil;
NSArray *arrayFromData = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSArray class] fromData:data error:&error];
I keep getting an error: 'NSCocoaErrorDomain - code: 4864' in my NSError variable and the NSKeyedUnarchiver keeps returning nil.
What am I doing wrong here?
The problem is requiringSecureCoding:NO
. You must use secure coding at this point. That means you must make Grade adopt NSSecureCoding, not NSCoding; and you must list all the classes you intend to unarchive in a set at unarchiving time.
So, Grade is now an NSObject<NSSecureCoding>
; I'll fill in a few blanks you omitted from your question:
@implementation Grade
+ (BOOL) supportsSecureCoding { return YES; }
- (instancetype)initWithName:(NSString*)name grade:(NSInteger)grade
{
self = [super init];
if(self) {
self.name = name;
self.grade = grade;
}
return self;
}
- (NSString*) description {
return [NSString stringWithFormat: @"name: %@, grade: %ld", self.name, self.grade];
}
// And the rest is as you have it:
-(void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeObject:self.name forKey:@"name"];
[coder encodeInt64:self.grade forKey:@"grade"];
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super init];
if(self)
{
self.name = [coder decodeObjectForKey:@"name"];
self.grade = [coder decodeInt64ForKey:@"grade"];
}
return self;
}
@end
And now, let's make an array of Grade, archive it, and unarchive it:
NSArray<Grade*> *array = @[
[[Grade alloc] initWithName: @"Matt" grade: 4],
];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject: array requiringSecureCoding:YES error: nil];
NSLog(@"archived data: %@", data);
NSError *error = nil;
NSSet *classesSet = [NSSet setWithObjects:[NSString class], [Grade class], [NSArray class], nil];
NSArray *arrayFromData = (NSArray*)[NSKeyedUnarchiver unarchivedObjectOfClasses: classesSet fromData: data error: &error];
NSLog(@"array: %@", arrayFromData);
Result:
archived data: {length = 279 ... }
array: (
"name: Matt, grade: 4"
)
Quod erat demonstrandum.