1

I am trying to add a key and a value to a dictionary then add this dictionary the user defaults and read back into a dictionary object. I have two questions that I would really appreciate any help in,

1) why is the dictionary being read from user defaults empty? Since I added a key and a value to the dictionary shouldn't those be saved to the dictionary I retrieve from user defaults?

let defaults = UserDefaults.standard;
var myDict = [String: String]()
myDict["key"] = "value"

defaults.setValue(myDict, forKey: "myDict")


let mydict2 = defaults.object(forKey: "myDict") as? [String: String] ?? [String:String]()
print(mydict2)

2) What can I do to this code if the dictionary stores a custom class that I created as a value or a key so if the dictionary was like this:

class Car {
  var engineSize: Int
  var color: String

  init() {
    engineSize = 2000
    color = "blue"
  }

}

class Boat {    
  var surfaceArea: Int
  var weight: Int

  init() {    
    surfaceArea = 3500
    weight = 4000
  }
}

var myDict = [Car: Boat]()

how can I save that second dict to user defaults and read it from there?

Thank you

EDIT:

This is the answer suggested by ebby94:

var myDict = [String:String]()
myDict["key"] = "value";

let data = NSKeyedArchiver.archivedData(withRootObject: myDict)
UserDefaults.standard.set(data, forKey: "myDict")


func foo()

{
  guard let archivedData = UserDefaults.standard.value(forKey: "myDict") as? Data
    else
    {
        print("failed1")
        return
    }
  guard var unarchivedDictionary = NSKeyedUnarchiver.unarchiveObject(with: archivedData) as? [String:String]
    else
    {
        print("failed2")
        return
    }

    print(unarchivedDictionary["key"]!)
}

foo()

However this prints failed1, I'm assuming the data wasn't archived correctly. Can this be because I'm running it in playground?

Kareem Aboughazala
  • 535
  • 1
  • 6
  • 15
  • 1
    First just make sure you test it in a real project (playground doesn't work) – Leo Dabus Jan 25 '17 at 02:40
  • 1
    Second you need to make your class NSCoding compliant. – Leo Dabus Jan 25 '17 at 02:40
  • 1
    Third use KeyedArchiever to convert your object to data – Leo Dabus Jan 25 '17 at 02:41
  • 1
    Also just use `set(yourObjectData, forKey: "theKey")` instead of setValue – Leo Dabus Jan 25 '17 at 02:43
  • 1
    Use UserDefault method data(forKey:) to load your data from disk – Leo Dabus Jan 25 '17 at 02:44
  • 2
    @LeoDabus Why post 6 comments? Post an answer or vote to close as a dupe (this must be a dupe). – rmaddy Jan 25 '17 at 02:47
  • The class isnt confront to `NSCoding` yet, you have to implement it to make all the property key-value encode and then archived everything into Data – Tj3n Jan 25 '17 at 05:22
  • @Tj3n I have to do that even for the string class? Can you guide me to any information source that would explain how that is done? – Kareem Aboughazala Jan 25 '17 at 05:32
  • You will need it doesnt matter the type, because its for the property name, not value, do a quick search it has been answered many times like [this](http://stackoverflow.com/questions/2315948/how-to-store-custom-objects-in-nsuserdefaults) – Tj3n Jan 25 '17 at 05:46

3 Answers3

3

You can't save a dictionary directly in UserDefaults. You'll have to convert the dictionary into data and save it and then retrieve the data and unarchive it into dictionary.

Archive and save to UserDefaults

    let myDict = [String:String]()
    let data = NSKeyedArchiver.archivedData(withRootObject: myDict)
    UserDefaults.standard.set(data, forKey: "myDict")

Retrieve and unarchive the data to dictionary

    guard let archivedData = UserDefaults.standard.value(forKey: "myDict") as? Data
        else{return}
    guard let unarchivedDictionary = NSKeyedUnarchiver.unarchiveObject(with: archivedData) as? [String:String]
        else{return}
ebby94
  • 3,131
  • 2
  • 23
  • 31
  • I'm not sure how userdefaults is handled in a playground. Can you try doing this in a new project and update the result? – ebby94 Jan 25 '17 at 05:40
3

If you want to save custom object to userDefault first you need to encode & decode variable then save using archive & get data using unarchive.

class Car {
    var engineSize: Int
    var color: String

    init() {
        engineSize = 2000
        color = "blue"
    }
    // Decode
    required convenience public init(coder decoder: NSCoder)
    {
        self.init()

        if let engineSize = decoder.decodeObject(forKey: "engineSize") as? Int
        {
            self.engineSize = engineSize
        }
        if let color = decoder.decodeObject(forKey: "color") as? String
        {
            self.color = color
        }
    }

    // Encode
    func encodeWithCoder(coder : NSCoder)
    {
        if let engineSize = self.engineSize
        {
            coder.encode(engineSize, forKey: "engineSize")
        }
        if let color = self.color
        {
            coder.encode(color, forKey: "weight")
        }
    }
}

class Boat {
    var surfaceArea: Int
    var weight: Int

    init() {
        surfaceArea = 3500
        weight = 4000
    }

    // Decode
    required convenience public init(coder decoder: NSCoder)
    {
        self.init()

        if let surfaceArea = decoder.decodeObject(forKey: "surfaceArea") as? Int
        {
            self.surfaceArea = surfaceArea
        }
        if let weight = decoder.decodeObject(forKey: "weight") as? Int
        {
            self.weight = weight
        }
    }

    // Encode
    func encodeWithCoder(coder : NSCoder)
    {
        if let surfaceArea = self.surfaceArea
        {
            coder.encode(surfaceArea, forKey: "surfaceArea")
        }
        if let weight = self.weight
        {
            coder.encode(weight, forKey: "weight")
        }
    }
Nimisha Ranipa
  • 8,304
  • 2
  • 14
  • 29
  • Thank you so much for your reply, is there anyway you can explain the code a little more. a lot of this is new to me and I would like to understand it decently before I use it. what's required convenience? for self.surfaceArea = surfaceArea would self be the decoder's object? – Kareem Aboughazala Jan 26 '17 at 12:50
0

Userdefaults not work in Playground. you need to implement and start it in an App in Simulator

muescha
  • 1,544
  • 2
  • 12
  • 22