I might be late to this party, but since I just spent hours on it, I’ll 
document this for anyone who hasn’t run into it yet.

If you’re using NSSecureCoding, there’s a problem decoding NSArray objects. You 
can’t use this:

        myArray = [coder decodeObjectForKey: @“myArray”];

and you can’t use this:

        myArray = [coder decodeObjectOfClass: [NSArray class] forKey: 
@“myArray”];

The error message in the resulting exception won’t be very helpful, but it 
means that the class of the array elements is invalid. What you actually need 
to do is this:

        myArray = [coder decodeObjectOfClasses: [NSSet setWithObjects: [NSArray 
class], [MyElementClass class], nil] forKey: @“myArray”];

Besides being obscure, this is also semantically incorrect, in that it appears 
to allow decoding of objects that are not strictly arrays of MyElementClass 
objects. (I guess this is not a security violation, since it doesn’t permit an 
attack to substitute objects of arbitrary classes, but it sure is annoying.]

Now, if you want to do this in Swift, you might be tempted to try the obvious 
translation:

        let classes = Set<AnyClass> (arrayLiteral: [… anything here… ])
        let myArray = coder.decodeObjectOfClasses (classes, forKey: “myArray”)

but that won’t work because AnyClass isn’t Hashable. So you might try (well, I 
tried) something like this:

        let classes = Set<NSObject> (arrayLiteral: [NSArray.self, 
MyElementClass.self])
        let myArray = coder.decodeObjectOfClasses (classes, forKey: “myArray”)

This compiles, but it fails at run-time, with a message saying it found an 
object of class ‘NSArray’, but only objects of classes ‘NSArray’ and 
‘MyElementClass’ are allowed. (!)

The solution is to fall back to an explicit NSSet object:

        let classes = NSSet (objects: NSArray.self, MyElementClass.self)
        let myArray = coder.decodeObjectOfClasses (classes, forKey: “myArray”)

There really needs to be (radar #24646135) API to decode a NSArray and specify 
the class (or classes) of the elements. In Obj-C, something like:

        myArray = [coder decodeArrayWithObjectsOfClass: [MyElementClass class] 
forKey: @“myArray”];

or in Swift:

        let myArray = coder.decodeArrayWithObjectsOfClass (MyElementClass.self, 
forKey: “myArray”)

This would be particular useful in Swift, because it would give you 
compile-time checking of the types in the assignment. (There is already a 
Swift-only version of ‘decodeObjectOfClass’ that’s generic, so you get  the 
“correct” return type.)



_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to