10

I'm having trouble finding the correct way to conform the the NSSecureCoding protocol in Swift, specifically when decoding objects that is an array of other objects.

I can't create an NSSet of class types in swift.

In Objective-C I would use

self.books = [aDecoder decodeObjectOfClasses:[NSSet setWithObjects:[NSArray class], [Book class], nil] forKey:@"books"];

in Swift I'm having issues creating the NSSet like this :

self.books = aDecoder.decodeObjectOfClasses(NSSet().setByAddingObject(NSArray.self).setByAddingObject(Book.self), forKey:"books")

Here's the error:

Type 'NSArray.Type' does not conform to protocol 'AnyObject'
aksh1t
  • 5,410
  • 1
  • 37
  • 55
Tim Sawtell
  • 307
  • 2
  • 7
  • 1
    NSSet(objects: [NSArray.self, Book.self]) – cahn Jun 10 '14 at 05:29
  • Could not find an overload for 'init' that accepts the supplied arguments. try it out in a playground class Book { } class Test { var set = NSSet(objects: [NSArray.self, Book.self]) } – Tim Sawtell Jun 10 '14 at 06:39
  • possible duplicate of [How to create a class array in Swift](http://stackoverflow.com/questions/24174510/how-to-create-a-class-array-in-swift) – user102008 Sep 10 '14 at 09:56
  • @TimSawtell: it should work in the latest versions – user102008 Sep 24 '14 at 02:23
  • @user102008 yep it now compiles fine but I get a run time exception: Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: 'value for key 'strings' was of unexpected class 'NSArray'. Allowed classes are '{( ( NSArray, NSString ) )} Radar filed with Apple (id: 17617664) . It's still open. – Tim Sawtell Sep 25 '14 at 02:13

3 Answers3

5

For simple objects:

self.myObject = aDecoder.decodeObjectOfClass(MyObject.self, forKey: "myObject")!

Since Swift 3.0:

self.myObject = aDecoder.decodeObject(of:MyObject.self, forKey: "myObject")!

Swift 4

aDecoder.decodeObject(of: [NSArray.self, Book.self], forKey: "books") as? [Book]

For nested objects:

NSSet(objects: [NSArray.self, Book.self])
stoffen
  • 570
  • 6
  • 14
1

Declaration

SWIFT

func decodeObjectOfClasses(_ classes: NSSet,
                    forKey key: String) -> AnyObject?

Since Swift 3.0:

func decodeObject<DecodedObjectType where DecodedObjectType : NSCoding, DecodedObjectType : NSObject>(of cls: DecodedObjectType.Type, forKey key: String) -> DecodedObjectType?

OBJECTIVE-C

- (id)decodeObjectOfClasses:(NSSet *)classes
                     forKey:(NSString *)key

let books: AnyObject? = aDecoder.decodeObjectOfClasses(NSSet().setByAddingObject(NSArray.self).setByAddingObject(Book.self), forKey:"books")
EckhardN
  • 469
  • 5
  • 11
gotnull
  • 26,454
  • 22
  • 137
  • 203
-1

Not exactly the same as when I originally posted the question, but I've achieved what I wanted by using typed collections as seen below

class Bookshelf: NSObject, NSSecureCoding {
    var authors = [Author]()
    var books = [Book]()

    override init () {
        super.init()
    }

    @objc func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(self.authors, forKey: "authors")
        aCoder.encodeObject(self.books, forKey: "books")
    }

    @objc required init?(coder aDecoder: NSCoder) {
        self.authors = (aDecoder.decodeObjectForKey("authors") as? [Author])!
        self.books = (aDecoder.decodeObjectForKey("books") as? [Book])!
    }

    @objc static func supportsSecureCoding() -> Bool {
        return true
    }
}
Tim Sawtell
  • 307
  • 2
  • 7
  • This `init(coder:)` function is not actually using secure coding. In order to decode securely it needs to know the class ahead of time—so you need to use `decodeObject(of:forKey:)`. More details on this here: http://nshipster.com/nssecurecoding/ – robotspacer Apr 21 '17 at 04:00