25

I have the following test code:

  func testSaveDictionary() {
    let userDefaults = NSUserDefaults.standardUserDefaults()
    var jo = [
      "a" : "1.0",
      "b" : "2.0"
    ]

    let akey = "aKey"
    userDefaults.setObject(jo, forKey: akey)
    var isOk = userDefaults.synchronize()

    var data0 = userDefaults.dictionaryForKey(akey)
    println(data0)

  }

The output of println(data0) is nil.

Anything wrong with my code? Is Swift Dictionary considered property list now or in the final release?

Sean
  • 1,923
  • 2
  • 22
  • 30

5 Answers5

31

Update for Swift 2, Xcode 7: As @atxe noticed, NSUserDefaults dictionaries are now mapped as [String, AnyObject]. This is a consequence of the Objective-C "lightweight generics" which allow to declare the Objective-C method as

- (NSDictionary<NSString *,id> *)dictionaryForKey:(NSString *)defaultName

(Default objects must be property lists and in particular the dictionary keys can only be strings.)

On the other hand, a Swift dictionary is bridged automatically if possible, so the original code from the question works (again):

let jo = [
    "a" : "1.0",
    "b" : "2.0"
]

let akey = "aKey"
// Swift 2:
userDefaults.setObject(jo, forKey: akey)
// Swift 3:
userDefaults.set(jo, forKey: akey)

Original answer for Swift 1.2: The user defaults can store NSDictionary objects. These are mapped to Swift as [NSObject : AnyObject]:

var jo : [NSObject : AnyObject] = [
    "a" : "1.0",
    "b" : "2.0"
] 
userDefaults.setObject(jo, forKey: akey)
var isOk = userDefaults.synchronize()

And note that dictionaryForKey() returns an optional, so you should check it for example with an optional assignment:

if let data0 = userDefaults.dictionaryForKey(akey) {
    print(data0)
} else {
    print("not set")
}

// Output: [b: 2.0, a: 1.0]
Martin R
  • 488,667
  • 78
  • 1,132
  • 1,248
  • 2
    right on! the key is to explicitly declare: var jo : [NSObject : AnyObject] as you mentioned above. – Sean Sep 09 '14 at 21:43
  • Another way around (in case for some reason you can't make the declaration explicit) would be to use "as NSDictionary": userDefaults.setObject((jo as NSDictionary), forKey: aKey) – winterized Nov 18 '14 at 15:55
  • 1
    In Swift 2.0 NSUserDefault dictionaries are mapped as [String : AnyObject]. Actually if you try to save a Dictionary with Int (for instance) as key, it fails in runtime. You can define your extension with a strong typed method to safely support and avoid a possible runtime error, although it would be nice to have such trivial method provided by Apple. – atxe Oct 17 '15 at 08:47
  • I would suggest editing the answer, so that the Swift 2.0 version was on top. – xinatanil Jan 29 '16 at 15:58
  • "in particular the dictionary keys can only be strings" got rid of my `UserDefaults` crash. Thanks! – BallpointBen Jun 30 '17 at 03:41
12

You need to convert it into NSData first. Something like this:

var data = NSKeyedArchiver.archivedDataWithRootObject(jo)
var userDefaults = NSUserDefaults.standardUserDefaults()
userDefaults.setObject(data, forKey:akey) 
Anthony Kong
  • 29,857
  • 33
  • 139
  • 244
4

I faced with this problem yesterday and filed a radar: Cannot store Dictionary in NSUserDefaults in iOS8. This issue is related only to iOS 8. Also I described a workaround for this case: Workaround for saving dictionary in NSUserDefaults. Works like a charm.

/// Save
NSUserDefaults.standardUserDefaults().setObject(NSKeyedArchiver.archivedDataWithRootObject(object), forKey: key)

/// Read
var data = NSUserDefaults.standardUserDefaults().objectForKey(key) as NSData
var object = NSKeyedUnarchiver.unarchiveObjectWithData(data) as [String: String]
Berriel
  • 8,463
  • 3
  • 25
  • 54
Tomasz Szulc
  • 4,081
  • 3
  • 38
  • 78
3

Follow this beautiful code below -> Swift 3

public func setDict(dict: NSDictionary) {
    let data = NSKeyedArchiver.archivedData(withRootObject: dict)
    let userDefaults = UserDefaults.standard
    userDefaults.set(data, forKey:"keyHere")
}


public func getDict() -> NSDictionary {
    let data = UserDefaults.standard.object(forKey: "keyHere") as! NSData
    let object = NSKeyedUnarchiver.unarchiveObject(with: data as Data) as! NSDictionary
    return object;
}
1

//swift 4.0

//for saving

    let userDefaults = UserDefaults.standard
    userDefaults.setValue(value, forKey: "Your_Key")
    userDefaults.synchronize()

//for for retrieve

        let loadedCart = UserDefaults.standard.dictionary(forKey: "Your_Key")
Shashi Verma
  • 3,360
  • 3
  • 10
  • 19
  • 1
    According to my understanding there are very few reasons for calling `userDefaults.synchronize()`. – Johan Aug 17 '17 at 21:50