1

I'm trying to save a nested dictionary in userDefaults. The app crashes when I try to save it the usual way, i.e.

defaults.set(totalBuy, forKey: "test")

and I get this error:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to insert non-property list object

And when I try convert it to NSData and then try retrieve it, it always comes back as nil.

Here is the code:

var buyData = [Int : String]()
var buyingTotal = [String : [Int: String]]()
var totalBuy = [Int : [String : [Int: String]]]()


let buyerDict = defaults.dictionary(forKey: "buyerDict")
let test = defaults.dictionary(forKey: "test")
func userDefaultSave(){

    buyData[0] = value
    buyData[1] = value
    buyData[2] = value
    buyData[3] = value
    
    buyingTotal["skuu"] = buyData
    totalBuy[0] = buyingTotal
    
    let data: Data = NSKeyedArchiver.archivedData(withRootObject: totalBuy) /// converts to NS Data
    defaults.set(data, forKey: "buyerDict")

    defaults.set(totalBuy, forKey: "test")
    if let dic = defaults.dictionary(forKey: "test") as? [Int : [String : [Int: String]]]  {
        print(dic)
    }

    let retrieved = defaults.object(forKey: "buyerDict") as! Data
    let dictionary: Dictionary? = NSKeyedUnarchiver.unarchiveObject(with: retrieved) as? [String : Any]
    
    print("dictionary--->", dictionary as Any)
}

Can anyone help me?

pkamb
  • 33,281
  • 23
  • 160
  • 191
Dan.code
  • 431
  • 2
  • 14
  • Possible duplicate of: [Attempt to set a non-property-list object as an NSUserDefaults](https://stackoverflow.com/questions/19720611/attempt-to-set-a-non-property-list-object-as-an-nsuserdefaults) – pkamb Sep 16 '20 at 06:10

2 Answers2

1

There are 2 ways you can get this working.

1. You can use JSONEncoder() and JSONDecoder() to get the data to and from the Dictionary object, i.e.

To get the data from totalBuy,

if let data = try? JSONEncoder().encode(totalBuy) {
    defaults.set(data, forKey: "buyerDict")
}

To get the Dictionary from data,

if let data = defaults.data(forKey: "buyerDict"), let dict = try? JSONDecoder().decode([Int:[String:[Int:String]]].self, from: data) {
    print(dict)
}

2. In case you still want to use NSKeyedArchiver and NSKeyedArchiver, here you go

To get the data from totalBuy,

let data = try? NSKeyedArchiver.archivedData(withRootObject: totalBuy)
defaults.set(data, forKey: "buyerDict")

To get the Dictionary from data,

if let data = defaults.data(forKey: "buyerDict") {
    let dict = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data)
    print(dict)
}

Use data(forKey:) instead of object(forKey:) when retrieving the data from UserDefaults.

PGDev
  • 23,751
  • 6
  • 34
  • 88
0

your dictionary need to change key Int to String Or convert dictionary to Data and retrieve it.

try this method to set and get your value from userdefault

let USERDEFAULT = UserDefaults.standard

class func setUserValueArchiver(value:Any, key :String) -> Void{
    guard !(value is NSNull) else {
        return
    }
    do{
        let archive = try NSKeyedArchiver.archivedData(withRootObject: value, requiringSecureCoding: true)
        USERDEFAULT.set(archive, forKey: key)
        USERDEFAULT.synchronize()
    }catch let error{
        Log("Error Save in UserDefault \(error.localizedDescription)")
    }
}

class func getUserUnArchiveData(key : String) -> Any?{
    if let userdata = USERDEFAULT.object(forKey: key) as? Data{
        do{
            let unarchived = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(userdata)
            return unarchived
        }catch let error{
            Log("Error At Get UserDATA :\(error.localizedDescription)")
        }
    }
    return nil
}
Dhawal
  • 1,055
  • 6
  • 10