How to update NSKeyedUnarchiver unarchiveObjectWithData to NSKeyedUnarchiver unarchivedObjectOfClass:[fromData:error:
Asked Answered
S

1

2

My app currently uses this deprecated function:

id unarchivedObject=[NSKeyedUnarchiver unarchiveObjectWithData:codedData];
if([unarchivedObject isKindOfClass:[NSDictionary class]]){
    // currently returns TRUE when reading existing user data.
} 

To update, I've converted to this:

id unarchivedObject=[NSKeyedUnarchiver unarchivedObjectOfClass:[NSDictionary class] fromData:codedData error:nil];
if([unarchivedObject isKindOfClass:[NSDictionary class]]){
    // currently returns FALSE when reading existing user data.
}

The data was originally encoded like this:

-(void)encodeWithCoder:(NSCoder*)encoder{
    [encoder encodeObject:text forKey:@"text"];
}
-(instancetype)initWithCoder:(NSCoder*)decoder{
    if(self=[super init]){
        text=[decoder decodeObjectForKey:@"text"];
}

What could be causing the IF statement to return FALSE using the newer code?

Please note that I am concerned primarily with reading existing data stored prior to deprecating the Archiving functions. Simply changing to the newer functions does not resolve the issue.

Strive answered 21/9, 2019 at 18:39 Comment(0)
S
1

Interesting question! I've been supporting iOS 10.0 so I haven't encountered such issue until I saw this. I was tinkering for an hour and I successfully found the issue.

What could be causing the IF statement to return FALSE using the newer code?

It's because your unarchivedObject object is nil!

If you use the parameter error in the new method, you would see an error like this:

Error Domain=NSCocoaErrorDomain Code=4864 "This decoder will only decode classes that adopt NSSecureCoding. Class 'QTPerson' does not adopt it." UserInfo={NSDebugDescription=This decoder will only decode classes that adopt NSSecureCoding. Class 'QTPerson' does not adopt it.

But how do we get the correct value for this unarchivedObject and not nil? It would take a couple of steps.

First off, make your model/class conform to <NSCoding, NSSecureCoding>

Example: QTPerson.h

#import <Foundation/Foundation.h>

@class QTPerson;

NS_ASSUME_NONNULL_BEGIN

#pragma mark - Object interfaces

@interface QTPerson : NSObject <NSCoding, NSSecureCoding>
@property (nonatomic, copy) NSString *text;
@end

NS_ASSUME_NONNULL_END

And then implement the protocol methods:

QTPerson.m

#import "QTPerson.h"

@implementation QTPerson

+ (BOOL)supportsSecureCoding {
    return YES;
}

- (void)encodeWithCoder:(NSCoder *)coder {
    [coder encodeObject:_text forKey:@"text"];
}

- (instancetype)initWithCoder:(NSCoder *)coder {
    self = [super init];
    if (self) {
        _text = [coder decodeObjectOfClass:[NSString class] forKey:@"text"];
    }
    return self;
}

@end

And then when archiving an object, you would want to pass YES to the parameter requiringSecureCoding, like so:

QTPerson *person = [[QTPerson alloc] init];
person.text = @"Glenn";

NSData *codedData1 = [NSKeyedArchiver archivedDataWithRootObject:person requiringSecureCoding:YES error:nil];
[[NSUserDefaults standardUserDefaults] setValue:codedData1 forKey:@"boom"];

Lastly, when unarchiving, just do what you did correctly, like so:

NSData *codedData = [[NSUserDefaults standardUserDefaults] dataForKey:@"boom"];

NSError *er;
id unarchivedObject=[NSKeyedUnarchiver unarchivedObjectOfClass:[QTPerson class] fromData:codedData error:&er];

if([unarchivedObject isKindOfClass:[QTPerson class]]){
    NSLog(@"TRUE!");
} else {
    NSLog(@"FALSE!");
}

Voila! You'll get nonnull object unarchivedObject, hence the TRUE/YES value you're looking for!

Strage answered 21/9, 2019 at 20:25 Comment(3)
Thanks for the code/research, however after implementing, I still get a null unarchived object when reading existing data - which is the real issue here. I could delete existing data for this app update and start fresh, however existing users of the app will lose their data which I would prefer not to do.Strive
@Strive Did you find a solution?Sejm
@DavidvanDugteren No, I have not resolved this yet. At this time I have pulled my Apps from the App Store.Strive

© 2022 - 2024 — McMap. All rights reserved.