Search code examples
iosswiftnsuserdefaults

Storing nested objects in NSUserDefaults


I'm trying to store custom codable struct with mutated property, but I'm always getting nil for that property.

For example, having codable struct:

struct Test1: Codable {
    var testDate: Date? = nil
    let name: String
    let age: Int

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

with the following example JSON we will decode provided struct, and assign custom property testDate:

let json = """
{
"name": "test",
"age": 30,
}
"""


let jsonData = Data(json.utf8)
var test1 = try? JSONDecoder().decode(Test1.self, from: jsonData)
test1?.testDate = Date()

Then we will try to store this struct in userDefaults:

var currentTest: Test1? {
    get {
        let defaults = UserDefaults.standard
        guard let testData = defaults.object(forKey: "test1") as? Data,
            let test = try? PropertyListDecoder().decode(Test1.self,
                                                          from: testData) else {
            return nil
        }

        return test
    }
    set {
        let defaults = UserDefaults.standard
        defaults.set(try? PropertyListEncoder().encode(newValue), forKey: "test1")
    }
}

While this works for all of the codable properties, when I try to access a custom property, such as testDate I'm getting nil:

currentTest?.testDate = nil

Is there a way to store "nested" properties without storing them as a separate instance in UserDefautls?

gist example - https://gist.github.com/ignotusverum/0cb9b57eef021eed3680530df519cedf


Solution

  • Since you have CodingKeys in Test1, you need to add case testDate or property testDate will be omitted when decoding instances.

    struct Test1: Codable {
        var testDate: Date? = nil
        let name: String
        let age: Int
    
        enum CodingKeys: String, CodingKey {
            case name
            case age
            case testDate
        }
    }
    

    Check Encoding and Decoding Custom Types,

    ...Codable types can declare a special nested enumeration named CodingKeys that conforms to the CodingKey protocol. When this enumeration is present, its cases serve as the authoritative list of properties that MUST be included when instances of a codable type are encoded or decoded. The names of the enumeration cases should match the names you've given to the corresponding properties in your type.