NSKeyedUnarchiver decodeObjectOfClasses failed

I tried to read an Object :

-(NSMutableDictionary*) readMyObject:(NSData*)data;
{
    NSError * error;
    Class class = [NSMutableDictionary class];
    NSMutableDictionary * dict;
    dict = [NSKeyedUnarchiver unarchivedObjectOfClass:class
                                           fromData:data error:&error];
    return dict;

the result was nil.

I searched by Developer for a solution and found one :

{
//    NSKeyedUnarchiver * unarchiver = [[NSKeyedUnarchiver alloc] init];
    [unarchiver decodeObjectOfClasses:
        [[NSSet alloc]initWithArray:
                 @[[NSDictionary class],
                        [NSMutableDictionary class],
                        [NSArray class],
                        [NSMutableArray class],
                        [NSString class], [NSNumber class]]]
                   forKey:NSKeyedArchiveRootObjectKey];
   
    [unarchiver finishDecoding];
}

The first line was from me and it crashed the project.

I assume there is an easy answer, not for me.🥲 Uwe

Accepted Reply

There are two cases to consider here. First, consider a custom object like this:

@interface MyObject : NSObject <NSSecureCoding>

@property (nonatomic, copy, readwrite, nullable) NSString * myProperty;

@end

You implement secure coding support like this:

+ (BOOL)supportsSecureCoding {
    return YES;
}

- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder { 
    self = [super init];
    if (self != nil) {
        self->_myProperty = [[coder decodeObjectOfClass:[NSString class] forKey:@"myProperty"] copy];
    }
    return self;
}

- (void)encodeWithCoder:(nonnull NSCoder *)coder {
    [coder encodeObject:self.myProperty forKey:@"myProperty"];
}

And then encode and decode like this:

MyObject * obj = [[MyObject alloc] init];
obj.myProperty = @"Hello Cruel World!";
NSData * archive = [NSKeyedArchiver archivedDataWithRootObject:obj requiringSecureCoding:true error: nil];
MyObject * obj2 = [NSKeyedUnarchiver unarchivedObjectOfClass:[MyObject class] fromData:archive error:nil];

The second case relates to collections, where you have to authorise the decoding of both the container and the items within the container. I have an example of this here, albeit one in Swift. Fortunately the take-home point — the need to use +unarchivedObjectOfClasses:fromData:error: — is pretty clear.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Replies

There are two cases to consider here. First, consider a custom object like this:

@interface MyObject : NSObject <NSSecureCoding>

@property (nonatomic, copy, readwrite, nullable) NSString * myProperty;

@end

You implement secure coding support like this:

+ (BOOL)supportsSecureCoding {
    return YES;
}

- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder { 
    self = [super init];
    if (self != nil) {
        self->_myProperty = [[coder decodeObjectOfClass:[NSString class] forKey:@"myProperty"] copy];
    }
    return self;
}

- (void)encodeWithCoder:(nonnull NSCoder *)coder {
    [coder encodeObject:self.myProperty forKey:@"myProperty"];
}

And then encode and decode like this:

MyObject * obj = [[MyObject alloc] init];
obj.myProperty = @"Hello Cruel World!";
NSData * archive = [NSKeyedArchiver archivedDataWithRootObject:obj requiringSecureCoding:true error: nil];
MyObject * obj2 = [NSKeyedUnarchiver unarchivedObjectOfClass:[MyObject class] fromData:archive error:nil];

The second case relates to collections, where you have to authorise the decoding of both the container and the items within the container. I have an example of this here, albeit one in Swift. Fortunately the take-home point — the need to use +unarchivedObjectOfClasses:fromData:error: — is pretty clear.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"