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

前端 未结 2 1519
感情败类
感情败类 2021-01-05 03:30

I am using unarchiveObjectWithData to unarchive data from NSUserDefaults and it is working good, but it was deprecated in iOS 12.0. Xcode suggests

相关标签:
2条回答
  • 2021-01-05 03:58

    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];
    
    0 讨论(0)
  • 2021-01-05 04:07

    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];
    
    0 讨论(0)
提交回复
热议问题