Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unarchive data with unarchivedObjectOfClass:fromData:error:?

I am using unarchiveObjectWithData to unarchive data from NSUserDefaults and it is working good, but it was deprecated in iOS 12.0. Xcode suggests to use unarchivedObjectOfClass:fromData:error:, but this method does not work.

I have an array of objects of my class to unarchive.

I have tried to use unarchivedObjectOfClass:fromData:error:, also unarchivedObjectOfClasses:fromData:error: with all classes (NSArray, NSString, MYCLASS..)

Works

NSArray *stored = [NSKeyedUnarchiver unarchiveObjectWithData:data];

Does not:

NSArray *stored = [NSKeyedUnarchiver unarchivedObjectOfClass:[MYCLASS class] fromData:data error:&error];

I am receiving ”The data couldn’t be read because it isn’t in the correct format.„

like image 822
Ion Batîr Avatar asked Mar 29 '19 14:03

Ion Batîr


2 Answers

unarchivedObjectOfClass:fromData:error seems to be pretty undocumented, but I have figured it out. In your case as you are unarchiving an NSArray and assuming the contents of the array are standard classes like NSString then this should work for you:

NSArray *stored = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSArray class] fromData:data error:&error];

However I was unarchiving a custom object PurchasedSubscription and this also applies if your NSArray contains any custom Classes... Firstly the objectOfClass from the method to unarchive needs to be the class of what you are expecting to be the result.

PurchasedSubscription *purchasedSubscription = [NSKeyedUnarchiver unarchivedObjectOfClass:PurchasedSubscription.class fromData:data error:&error];

Next, your custom class needs to conform to NSSecureCoding so add this to the interface of the class. I assume you already have NSCoding implemented.

@interface PurchasedSubscription : NSObject <NSCoding, NSSecureCoding>

@end

Next your class has to override supportsSecureCoding to confirm it is supported

+ (BOOL)supportsSecureCoding
{
    return YES;
}

Next, in your initWithCoder: method, you need to use decodeObjectOfClass:key: instead of decodeObjectForKey when decoding each property, again setting the Class parameter as the class type of what's being decoded.

- (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder
{
    self = [self init];

    if (self)
    {
        ReceiptInfo *receiptInfo = [aDecoder decodeObjectOfClass:[ReceiptInfo class] forKey:@"receiptInfo"];
        return [self initWithReceiptInfo:receiptInfo];
    }

    return self;
}

As you can see here, this class also decodes another custom class ReceiptInfo, so I had to repeat this process with that class to get it all to work.

Now when I use

PurchasedSubscription *purchasedSubscription = [NSKeyedUnarchiver unarchivedObjectOfClass:PurchasedSubscription.class fromData:data error:&error];

it securely decodes the PurchasedSubscription class by securely decoding the ReceiptInfo class as it knows at each step what the class type should be before it decodes it.


A note on the opposite NSEncoding. You need to use the method

archivedDataWithRootObject:requiringSecureCoding:error:

instead of

archivedDataWithRootObject:

With this one, you don't pass in the object class, you pass in the actual object. In my case, I create the object like so

PurchasedSubscription *validSub = [[PurchasedSubscription alloc] initWithReceiptInfo:latestReceipt];

and then encode it like this

NSData *data = [NSKeyedArchiver archivedDataWithRootObject:validSub requiringSecureCoding:YES error:&error];
like image 143
Darren Avatar answered Nov 15 '22 21:11

Darren


I faced up with an issue and follow the @Darren answer, but unarchivedObjectOfClass returns nil ! :(

so i tried a lot of variation and found the only success way is my case:

supportsSecureCoding property must be YES

and

NSArray<MyClassConformedToNSObject_NSCoding_NSSecureCoding *> *arrayOfCustomObjects = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSObject class] fromData:data error:&error];
like image 3
Eugene Avatar answered Nov 15 '22 22:11

Eugene