objective-cnscodingnskeyedarchivernssecurecoding

Why is NSArray of NSNumber(s) failing to decode?


I have a custom class that conforms to NSSecureCoding. I'm trying to encode its data, write it to a file, and decode it. The class contains an int array which I convert to an NSArray filled with NSNumber elements before encoding. When I decode the array, decodeObjectForKey returns nil. Also, when I write the encoded array to file, the plist doesn't look right. For example, it doesn't have the "key_for_ns_array" key anywhere in its structure.

Here's a simplified version of my code:

@interface c_my_data_wrapper : NSObject <NSSecureCoding>
@property (assign, nonatomic) struct s_my_data my_data; // the array exists as an int[] in here
@end
@implementation c_my_data_wrapper

- (void)encodeWithCoder:(NSCoder *)encoder
{
    NSMutableArray *ns_array= [NSMutableArray array];
    int_array_to_ns_array(self.my_data.my_int_array, count, ns_array);

    [encoder encodeObject:ns_array forKey:@"key_for_ns_array"];
}

- (instancetype)initWithCoder:(NSCoder *)decoder
{
    self= [self init];
    
    if (self)
    {
        // this returns nil
        NSArray *ns_array= [decoder decodeObjectForKey:@"key_for_ns_array"];
    }

    return self;
}

@end
c_secure_storage_data_wrapper* g_my_data_wrapper= nil; // allocation not shown
void store_data(struct s_storage_data_secure *new_data)
{
    g_my_data_wrapper.my_data= *new_data;

    NSData *encoded_data=
        [NSKeyedArchiver archivedDataWithRootObject:g_my_data_wrapper
                              requiringSecureCoding:YES
                                              error:nil];

    // then I write the encoded data to a file
}
void load_data(void)
{
    NSData *decoded_data= [NSData dataWithContentsOfURL:my_url];

    g_my_data_wrapper=
        [NSKeyedUnarchiver unarchivedObjectOfClass:[c_my_data_wrapper class]
                                          fromData:decoded_data
                                             error:nil];
}
void int_array_to_ns_array(
    int *int_array,
    int count,
    NSMutableArray *out_ns_array)
{
    for (int index= 0; index < count; ++index)
    {
        [out_ns_array addObject:[NSNumber numberWithInt:int_array[index]]];
    }
}

If you're curious about the whole int array to ns array conversion thing... it's because I prefer to write in c++. I'm not very experienced with objective-c, so I only write it when I need to bridge the gap between my game and iOS. In this case, s_my_data is a type that's shared between the two.


Solution

  • One problem, probably the problem, is that you claim your class is NSSecureCodeing-compliant, but you're still calling -decodeObjectForKey: instead of -decodeObjectOfClass:forKey::

    NSArray *ns_array= [decoder decodeObjectForKey:@"key_for_ns_array"];
    

    That's not secure. The whole point of NSSecureCoding is that you tell the decoder what type you're expecting to get back, which is what -decodeObjectOfClass:forKey: does.