0

I have a class named Person and created a Person instance "person".

Person *person = [Person personWithName:@"Kyle", andAge:15];

Then I tried to encode it using method archivedDataWithRootObject:requiringSecureCoding:error:.

NSData *personData = [NSKeyedArchiver archivedDataWithRootObject:person 
                                      requiringSecureCoding:YES error:nil];

However, the personData always returns nil. Did I miss something?

Person.h

@interface Person : NSObject<NSSecureCoding>
@property (strong, nonatomic) NSString *name;
@property (assign, nonatomic) NSInteger age;
+ (instancetype)personWithName:(NSString *)name andAge:(NSInteger)age;
@end

Person.m

@implementation Person
+ (instancetype)personWithName:(NSString *)name andAge:(NSInteger)age{
    Person *p = [Person new];
    p.name = name;
    p.age = age;
    return p;
}
+ (BOOL)supportsSecureCoding {
    return YES;
}
- (id)initWithCoder:(NSCoder *)coder {
    self = [super initWithCoder:coder]; // error: No visible @interface for 'NSObject' declares the selector 'initWithCoder'
    return self;
}
@end

Update (after implementing +supportsSecureCoding in .m):

Class 'Person' has a superclass that supports secure coding, but 'Person' overrides -initWithCoder: and does not override +supportsSecureCoding. The class must implement +supportsSecureCoding and return YES to verify that its implementation of -initWithCoder: is secure coding compliant.

大陸北方網友
  • 3,696
  • 3
  • 12
  • 37
  • And if you use the `error` parameter instead of passing `nil`, does it give more infos on why it failed? Also, do you have error message in console? Is `Person` `NSCoding` (and `NSSecureCoding`) compliant? – Larme Apr 01 '21 at 08:48
  • @Larme It told me that "This decoder will only decode classed that adopt NSSecureCoding. Class 'Person' does not adopt it." Then I [implement +supportsSecureCoding in .m](https://stackoverflow.com/a/61102226/8335151),but get another error (have updated in description). – 大陸北方網友 Apr 01 '21 at 09:02
  • `NSKeyedArchiver` is linked to `NSCoding`, else, it's doesn't know how to transform that class into `NSData` and reverse. Now, there is the "Secure thing", it's almost the same. You need to override `supportsSecureCoding` to return `YES` as said in the error message. It's pretty explicit. No? How did you write that? Can you share the code? – Larme Apr 01 '21 at 09:08
  • @Larme Have posted the code. TIA:) – 大陸北方網友 Apr 01 '21 at 09:20
  • You didn't write `initWithCoder`? – Larme Apr 01 '21 at 09:21
  • @Larme If write `initWithCoder`, I get the error `No visible @interface for 'NSObject' declares the selector 'initWithCoder'`. – 大陸北方網友 Apr 01 '21 at 09:25
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/230626/discussion-between-larme-and-kyle-wang). – Larme Apr 01 '21 at 09:28
  • See https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Archiving/Articles/codingobjects.html#//apple_ref/doc/uid/20000948-BCIHBJDE – Larme Apr 01 '21 at 09:32

1 Answers1

0

What's wrong: Person isn't NSSecureCoding compliant. That's the first thing that pops in mind if you ever played with Archiving Custom Class into Data with NSKeyedArchiver (if it's an old developer, he/she would have said NSCoding, but that's "almost the same", same logic).
What's the deal with that? It's just how to convert Person into NSData and reverse. What's the logic? Do you want to save its properties? How? Etc.

BUT, your biggest mistake as a developer was to totally ignore the error parameter!

NSData *personData = [NSKeyedArchiver archivedDataWithRootObject:person 
                                      requiringSecureCoding:YES error:nil];

==>

NSError *archiveError
NSData *personData = [NSKeyedArchiver archivedDataWithRootObject:person 
                                      requiringSecureCoding:YES
                                      error:& archiveError];
if (archiveError) {
    NSLog(@"Ooops, got error while archiving: %@", archiveError);
}

Then the error would have stated that it was indeed missing the NSSecureCoding compliancy

See the documentation of Archives and Serializations Programming Guide: Encoding and Decoding Objects, you'll see how to implement initWithCoder: (from NSData to Person) and encodeWithCoder: (from Person to NSData).

Applied to your class (and add it to the compliance with: @interface Person : NSObject< NSSecureCoding > for instance):

- (void) encodeWithCoder:(NSCoder *)encoder { 
    [encoder encodeObject:_name forKey:@"name"]; 
    [encoder encodeInteger:_age forKey:@"age"]; 
} 

- (id)initWithCoder:(NSCoder *)coder { 
    self = [super init]; 
    if (self) { 
        _name = [coder decodeObjectForKey:@"name"]; 
        _age = [coder decodeIntegerForKey:@"age"]; 
    } 
    return self; 
}

+ (BOOL)supportsSecureCoding {
    return YES;
}

Note that the strings key ("name" & "age" needs to be the same for encode/decode, you can use const, etc.)

Larme
  • 24,190
  • 6
  • 51
  • 81