Eli Waxman
Eli Waxman

Reputation: 2897

NSKeyedArchiver can not archive an object

I have a JSON file I am reading from disk

This is the structure of the file:

[
{
    "name": "3X",
    "priority": 33
},
{
    "name": "4X",
    "priority": 32
}
]

I am decoding it to a class called Test

class TestClass: NSObject, Codable {

    enum CodingKeys: String, CodingKey {
        case name, priority
    }

    let name:String
    let priority:Int

    var optional:String? = nil
    var problem:String = "eli"

    init(name:String, priority:Int, optional:String? = nil, problem:String) {
        self.name = name
        self.priority = priority
        self.optional = optional
        self.problem = problem
        super.init()
    }

}

This is the code I am using to decode the file

    if let path = Bundle.main.path(forResource: "test", ofType: "json") {
        do {
            let jsonData = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
            let testObjects = try JSONDecoder().decode([TestClass].self, from: jsonData)
            let data = try! NSKeyedArchiver.archivedData(withRootObject: testObjects, requiringSecureCoding: false)
            UserDefaults.standard.set(data, forKey: "test_objects")
        } catch {
            // error in parsing json
        }
    }

After decoding I am trying to save the array into disk, I am converting it into NSData for saving however NSKeyedArchiver.archivedData fails with the following error:

{NSDebugDescription=Caught exception during archival: -[BugTesting.TestClass encodeWithCoder:]: unrecognized selector sent to instance 0x600002119ac0

To me this looks very strange since I am conforming to Codable.

Any help would be appreciated.

Upvotes: 1

Views: 718

Answers (1)

Rob
Rob

Reputation: 437682

If you want to encode this, I might suggest PropertyListEncoder. Then you don’t need to introduce the NSObject (nor the NSCoding/NSSecureCoding cruft) associated with NSKeyedArchiver:

class TestClass: Codable {
    enum CodingKeys: String, CodingKey {
        case name, priority
    }

    let name: String
    let priority: Int

    var optional: String?
    var problem: String?

    init(name: String, priority: Int, optional: String? = nil, problem: String? = nil) {
        self.name = name
        self.priority = priority
        self.optional = optional
        self.problem = problem
    }
}

Then to encode:

let data = try PropertyListEncoder().encode(test)

And to decode:

let test2 = try PropertyListDecoder().decode(TestClass.self, from: data)

I’m not sure why you wouldn’t encode optional or problem. I’d probably be inclined to code those too, unless there was some reason why you wanted to lose that data during the encoding/decoding process. If you want all the properties encoded, you can just eliminate the CodingKeys:

class TestClass: Codable {
    let name: String
    let priority: Int

    var optional: String?
    var problem: String?

    init(name: String, priority: Int, optional: String? = nil, problem: String? = nil) {
        self.name = name
        self.priority = priority
        self.optional = optional
        self.problem = problem
    }
}

Upvotes: 2

Related Questions