NSKeyedArchiver: casting Data returning nil - Swift

Andrew V. Jackson

Well, investigated several similar topics here, done everything as suggested, but my computed property "previousUserData" returns me nil, when trying to cast the decoded object to my type. What's wrong?

@objc class PreviousUserData: NSObject, NSCoding {

var name: String
var phone: String
var email: String

func encode(with aCoder: NSCoder) {
    aCoder.encode(name, forKey: "name")
    aCoder.encode(phone, forKey: "phone")
    aCoder.encode(email, forKey: "email")
}

required convenience init?(coder aDecoder: NSCoder) {
    guard
        let name = aDecoder.decodeObject(forKey: "name") as? String,
        let phone = aDecoder.decodeObject(forKey: "phone") as? String,
        let email = aDecoder.decodeObject(forKey: "email") as? String
        else {
            return nil
    }
    self.init(name: name, phone: phone, email: email)
}

init(name: String, phone: String, email: String) {
    self.name = name
    self.phone = phone
    self.email = email
}
}

unarchived returns me nil, but data for key "userdata" is exists

    var previousUserData: PreviousUserData? {
    get {
        if let object = UserDefaults.standard.object(forKey: "userdata") as? Data {
            let unarchived = NSKeyedUnarchiver.unarchiveObject(with: object) as? PreviousUserData
            return unarchived
        }
        return nil
    }
    set {
        let encodedData: Data = NSKeyedArchiver.archivedData(withRootObject: previousUserData as Any)
        UserDefaults.standard.setValue(encodedData, forKey: "userdata")
    }
    }
vadian

Actually you can't get valid data because the setter is wrong. You have to save newValue rather than previousUserData.

This is an slightly optimized version

var previousUserData: PreviousUserData? {
    get {
        guard let data = UserDefaults.standard.data(forKey: "userdata") else { return nil }
        return NSKeyedUnarchiver.unarchiveObject(with: data) as? PreviousUserData
    }
    set {
        guard let newValue = newValue else { return }
        let encodedData = NSKeyedArchiver.archivedData(withRootObject: newValue)
        UserDefaults.standard.set(encodedData, forKey: "userdata")
    }
}

NSCoding is pretty heavy. In this case I'd recommend to use Codable to serialize the data as JSON or Property List. It gets rid of @objc, class and NSObject and reduces the entire code to

struct PreviousUserData : Codable {
    var name: String
    var phone: String
    var email: String
}

var previousUserData: PreviousUserData? {
    get {
        guard let data = UserDefaults.standard.data(forKey: "userdata") else { return nil }
        return try? JSONDecoder().decode(PreviousUserData.self, from: data)
    }
    set {
        guard let newValue = newValue, let encodedData = try? JSONEncoder().encode(newValue) else { return }
        UserDefaults.standard.set(encodedData, forKey: "userdata")
    }
}

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related