1

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.

Eli Braginskiy
  • 2,867
  • 5
  • 31
  • 46
  • 1
    `Swift.Codable` and `Foundation.NSCoding` are distinct. You've conformed to `Codable` (and got a compiler-synthesized implementation of `init(from:)` and `encode(to:)`, but you're trying to use `NSKeyedArchiver`, which relies on `NSCoding`. Hence the error about the selector `encodeWithCoder:` being unrecognized by `TestClass`. – Alexander Mar 21 '20 at 16:50
  • 1
    And rather than storing it in `UserDefaults`, I might suggest just storing the plist (or just the json) in the application support directory, or something like that. This looks suspiciously like model data, not actual user defaults. – Rob Mar 21 '20 at 17:16

1 Answers1

2

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
    }
}
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Thanks, regarding your second comment, ``problem`` can't be optional, if you look at my code it is not optional, and then if I don't have it in the JSON the decoding from JSON fails. – Eli Braginskiy Mar 21 '20 at 18:29