17

I am having trouble with adopting NSSecureCoding. I encode an array containing objects of my custom class, which adoptsNSSecureCoding properly. When I decode it, passing the classNSArray (which is the class of the object I encoded), it throws an exception. However, when do theexact same thing with an array of strings, it works fine. I fail to see what is the difference between my class and NSString.

#import <Foundation/Foundation.h>@interface Foo : NSObject <NSSecureCoding>@end@implementation Foo- (id)initWithCoder:(NSCoder *)aDecoder {  return [super init];}- (void)encodeWithCoder:(NSCoder *)aCoder {}+ (BOOL)supportsSecureCoding {  return YES;}@endint main() {  @autoreleasepool {    NSMutableData* data = [[NSMutableData alloc] init];    NSKeyedArchiver* archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];    [archiver encodeObject:@[[Foo new]] forKey:@"foo"];    [archiver encodeObject:@[@"bar"] forKey:@"bar"];    [archiver finishEncoding];    NSKeyedUnarchiver* unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];    unarchiver.requiresSecureCoding = YES;    // throws exception: 'value for key 'NS.objects' was of unexpected class 'Foo'. Allowed classes are '{( NSArray )}'.'    [unarchiver decodeObjectOfClass:[NSArray class] forKey:@"foo"];    // but this line works fine:    [unarchiver decodeObjectOfClass:[NSArray class] forKey:@"bar"];    [unarchiver finishDecoding];  }  return 0;}
askedJun 24, 2014 at 0:49
user102008's user avatar
1

3 Answers3

18

My solution was to usedecodeObjectOfClasses:forKey:

In Swift:

if let data = defaults.objectForKey(FinderSyncKey) as? NSData    let unArchiver = NSKeyedUnarchiver(forReadingWithData: data)    unArchiver.setRequiresSecureCoding(true)     //This line is most likely not needed, I was decoding the same object across modules    unArchiver.setClass(CustomClass.classForCoder(), forClassName: "parentModule.CustomClass")    let allowedClasses = NSSet(objects: NSArray.classForCoder(),CustomClass.classForCoder())    if let unarchived = unArchiver.decodeObjectOfClasses(allowedClasses, forKey:NSKeyedArchiveRootObjectKey) as?  [CustomClass]{        return unarchived    }    }

In Objective-C it would be something like:

[unArchiver decodeObjectOfClasses:allowedClasses forKey:NSKeyedArchiveRootObjectKey]

The change in decode object to decode objects solved the above exception for me.

pkamb's user avatar
pkamb
35.5k27 gold badges174 silver badges207 bronze badges
answeredNov 10, 2014 at 3:13
utahwithak's user avatar
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for this! My Objective-C version looks like:[coder decodeObjectOfClasses:[NSSet setWithObjects:[NSArray class], [MyCustomClass class], nil] forKey:key]; Very poor documentation on Apple's part, but works like a charm!
Thank you so much.
Thank you! Really helped fixing a warning for iOS 15.
8

I was really struggling with this for an hour in Swift 5.

My situation was I had a Set of custom Solution objects:

var resultsSet = Set<Solution>()

Which conformed to Secure Coding:

static var supportsSecureCoding: Bool{ get{ return true } }

Their container object encoded them as an NSSet, because of Secure Coding:

aCoder.encode(resultsSet as NSSet, forKey: "resultsSet")

But I always got a complier error during decoding:

if let decodedResultsSet = aDecoder.decodeObject(of: NSSet.self, forKey: "resultsSet"){    resultsSet = decodedResultsSet as! Set<Solution>}

Error:

2020-02-11 22:35:06.555015+1300 Exception occurred restoring state value for key 'NS.objects' was of unexpected class 'App.Solution'. Allowed classes are '{(NSSet)}'.2020-02-11 22:35:06.564758+1300 *** Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: 'value for key 'NS.objects' was of unexpected class 'App.Solution'. Allowed classes are '{(NSSet)}'.'

If I changed the decodeObject(ofClass: to Solution:

if let decodedResultsSet = aDecoder.decodeObject(of: Solution.self, forKey: "resultsSet"){    resultsSet = decodedResultsSet as! Set<Solution>}

I get the error:

2020-02-11 22:33:46.924580+1300 Exception occurred restoring state value for key 'resultsSet' was of unexpected class 'NSSet'. Allowed classes are '{(app.Solution)}'.2020-02-11 22:33:46.933812+1300 *** Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: 'value for key 'resultsSet' was of unexpected class 'NSSet'. Allowed classes are '{(app.Solution)}'.'

The answer, which is obvious now, but I could find it anywhere was to realise the allowed object list is an array, and it needs both NSSet and the custom object: Solution.

if let decodedResultsSet = aDecoder.decodeObject(of: [NSSet.self, Solution.self], forKey: "resultsSet"){    resultsSet = decodedResultsSet as! Set<Solution>}
pkamb's user avatar
pkamb
35.5k27 gold badges174 silver badges207 bronze badges
answeredFeb 11, 2020 at 10:08
Matt C's user avatar

Comments

0

I had the same problem in Swift with iOS 15 / Xcode 13.2.I solved it by simply adding NSNumber (the offending class listed in the warning) in the decoder.decodeObject of required init(coder decoder: NSCoder) { }(I edited the class names). MyVar contains some numbers.

required init(coder decoder: NSCoder) {        myVar = decoder.decodeObject(of: [MyType.self, NSNumber.self], forKey: theKey) as? [MyVar] ?? []}
answeredFeb 2, 2022 at 21:25
claude31's user avatar

Comments

Your Answer

Sign up orlog in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

By clicking “Post Your Answer”, you agree to ourterms of service and acknowledge you have read ourprivacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.