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.„
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];
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];
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With