Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

NSCoder: how to encode/decode [Data] array

I’m having troubles encoding/decoding an array of Data (basically file bookmarks) in NSCoder protocol.

Here’s what I’m trying to do:

class ClosedFiles: NSCoder {
    static var supportsSecureCoding = true
    let bookmarks: [Data]
    init?(with coder: NSCoder) {
        if let bm = coder.decodeObject() as? [Data] {
            bookmarks = bm
        } else {
            return nil
        }
    }
    init(bookmarks: [Data]) {
        self.bookmarks = bookmarks
    }
    
    func encode(with coder: NSCoder) {
        coder.encodeRootObject(bookmarks)
    }
}

I encode this object in my app delegate:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

func application(_ app: NSApplication, willEncodeRestorableState coder: NSCoder) {
    coder.encodeRootObject(ClosedFiles(bookmarks: closedFiles))
}

This throws the error in runtime:

Ignoring exception: This decoder will only decode classes that adopt
NSSecureCoding. Class ‘_TtCC7MyApp11AppDelegate11ClosedFiles’ does
not adopt it.

I’m following the apple’s doc to conform to NSSecure, but it doesn’t seem to work.
I tried a few other method, but nothing seems working: coder.encode(), coder.encodeConditionalObject, and whatever else I found.

Any thought what I’m doing wrong?

>Solution :

You need a lot of changes for your class to support NSCoding (actually NSSecureCoding.

  • Your class must extend NSObject
  • Your class needs to conform to NSSecureCoding
  • You need the correct signature for the init(coder:) initializer
  • The supportsSecureCoding property needs to be static
  • The implementation of the two NSCoding methods need to be written correctly to support secure coding.

Here’s your class with all of the changes.

class ClosedFiles: NSObject, NSSecureCoding {
    let bookmarks: [Data]

    required init?(coder: NSCoder) {
        if let bm = coder.decodeArrayOfObjects(ofClass: NSData.self, forKey: "bookmarks") as? [Data] {
            bookmarks = bm
        } else {
            return nil
        }
    }

    init(bookmarks: [Data]) {
        self.bookmarks = bookmarks
    }

    func encode(with coder: NSCoder) {
        coder.encode(bookmarks as [NSData], forKey: "bookmarks")
    }

    static var supportsSecureCoding: Bool {
        return true
    }
}

I tested these changes in a Playground using the following code:

do {
    let closed = ClosedFiles(bookmarks: [ "Hello".data(using: .utf8)!, "There".data(using: .utf8)! ])
    let data = try NSKeyedArchiver.archivedData(withRootObject: closed, requiringSecureCoding: true)
    let dupe = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [ClosedFiles.self, NSArray.self, NSData.self], from: data) as! ClosedFiles
    let str1 = String(data: dupe.bookmarks[0], encoding: .utf8)!
    print(str1)
} catch {
    print(error)
}

This gives back the expected results.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading